Linux和Windows下文件和目录的相关属性及操作

本文介绍了在Linux和Windows系统下,如何使用C/C++进行文件和目录的属性读取操作。主要涉及stat结构体、stat函数、目录读取方法,包括Linux的opendir/readdir等和Windows的_findfirst/_findnext等函数。同时,文章讨论了不同操作系统下文件类型的判断和文件描述符的概念。
部署运行你感兴趣的模型镜像

我们知道C/C++都提供了标准的文件I/O库以便我们对文件进行读写。但我们无法通过标准的I/O库对文件系统进行更进一步的操作。因为这设计到具体操作系统中文件系统的设计。在Linux和Windows程序库下面都有一个sys目录,里面包含系统相关的头文件,如下:

Windows下:

C:\Program Files\Microsoft Visual Studio 9.0\VC\include\sys>dir
 驱动器 C 中的卷是 wins
 卷的序列号是 6C27-6F61
 C:\Program Files\Microsoft Visual Studio 9.0\VC\include\sys 的目录

2013-08-26  22:50    <DIR>          .
2013-08-26  22:50    <DIR>          ..
2002-05-29  04:48               997 locking.h
2007-01-06  08:59             6,722 stat.h
2004-01-09  22:27             1,856 stat.inl
2007-01-06  08:59             3,139 timeb.h
2004-03-30  06:34             1,414 timeb.inl
2006-11-09  01:45             2,033 types.h
2007-01-06  08:59             3,805 utime.h
2004-11-13  22:45             2,881 utime.inl
2006-11-09  01:45             1,917 wstat.inl
               9 个文件         24,764 字节
               2 个目录 11,852,636,160 可用字节

Linux下面:

[anonymalias@localhost ~]$ ls /usr/include/sys
acct.h       io.h           ptrace.h     stat.h         ucontext.h
acl.h        ipc.h          queue.h      statvfs.h      uio.h
asoundlib.h  kdaemon.h      quota.h      swap.h         ultrasound.h
bitypes.h    kd.h           raw.h        syscall.h      un.h
cdefs.h      klog.h         reboot.h     sysctl.h       unistd.h
debugreg.h   mman.h         reg.h        sysinfo.h      user.h
dir.h        mount.h        resource.h   syslog.h       ustat.h
epoll.h      msg.h          select.h     sysmacros.h    utsname.h
errno.h      mtio.h         sem.h        termios.h      vfs.h
eventfd.h    param.h        sendfile.h   timeb.h        vlimit.h
fcntl.h      pci.h          shm.h        time.h         vt.h
file.h       perm.h         signalfd.h   timerfd.h      vtimes.h
fsuid.h      personality.h  signal.h     times.h        wait.h
gmon.h       poll.h         socket.h     timex.h        xattr.h
gmon_out.h   prctl.h        socketvar.h  ttychars.h
inotify.h    procfs.h       soundcard.h  ttydefaults.h
ioctl.h      profil.h       statfs.h     types.h

我们可以看到在Windows和Linux下面的sys目录中都存在一个stat.h头文件,也是仅有的一个相同的文件。

Linux和Windows include目录下的sys/stat.h头文件都主要描述了对文件和目录的属性信息进行读取的操作。主要是通过struct stat结构体和stat()函数来实现的。在Windows的sys/stat.h头文件中的开头有下面一段说明:

/***
*sys/stat.h - defines structure used by stat() and fstat()
*       Copyright (c) Microsoft Corporation. All rights reserved.
*Purpose:
*       This file defines the structure used by the _stat() and _fstat()
*       routines.
*       [System V]
*       [Public]
****/

可以看出该头文件遵循System V标准,所以Windows下的stat结构和Linux下面的基本属性都一致,所以下面主要以Linux下符合POSIX标准的stat进行介绍。

1 stat结构

stat结构如下(从linux的<bits/stat.h>中截取,成员类型名有所修改,主要是删除了有些下划线):

struct stat
{
	dev_t st_dev;				/* 设备编号(文件系统)*/
	ino_t st_ino;				/* 文件索引节点的编号 */
	mode_t st_mode;				/* 文件的类型和访问权限 */
	nlink_t st_nlink;			/* 硬链接计数*/
	uid_t st_uid;				/* 文件所有者的用户ID */
	gid_t st_gid;				/* 文件所有者的组ID*/
	dev_t st_rdev;				/* 设备编号(特殊文件) */
	off_t st_size;				/* 文件大小(B) */
	blksize_t st_blksize;			/* 块大小(文件系统的I/O 缓冲区大小,最佳I/O块大小)  */
	blkcnt_t st_blocks;			/* 文件的块数*/
	time_t st_atime;			/* 最后访问时间*/
	time_t st_mtime;			/* 文件内容最后修改时间*/
	time_t st_ctime				/* 文件状态最后修改时间*/
};

上面的rdev,st_blksizest_blocks都不属于POSIX标准,且后面两个不存在与Windows的头文件中。

其中我们最常使用的属性有以下两个字段:st_mode和st_size。其中通过st_mode字段可以判断文件的类型:普通文件,目录,管道文件等。st_size用了查看一个文件的大小。

关于文件类型和访问权限字段st_mode使用位来标识每一种属性,该字段是一个16位的整数,Linux下,对于该字段的值在<sys/stat.h><bits/stat.h>中进行了定义,主要有以下属性:

/* Encoding of the file mode.  */

//掩码值都是8进制整数(即最高位0标识8进制),16位整数需要6位8进制数, 掩
//码值用到的位数为:0177777

#define S_IFMT        0170000 /* 高4位标识文件类型 */

/* File types.  */
#define S_IFDIR       0040000 /* 目录  */
#define S_IFCHR       0020000 /* 字符设备文件  */
#define S_IFREG       0100000 /* 普通文件  */
#define S_IFIFO       0010000 /* FIFO文件 */
#define S_IFBLK       0060000 /* 块设备文件  */
#define S_IFLNK       0120000 /* 符号链接文件  */
#define S_IFSOCK      0140000 /* socket文件  */

/* Protection bits.  */
#define S_ISUID       04000   /* Set user ID on execution.  */
#define S_ISGID       02000   /* Set group ID on execution.  */
#define S_ISVTX       01000   /* Save swapped text after use (sticky).  */
#define S_IREAD       0400    /* Read by owner.  */
#define S_IWRITE      0200    /* Write by owner.  */
#define S_IEXEC       0100    /* Execute by owner.  */

#define S_IRUSR S_IREAD       /* Read by owner.  */
#define S_IWUSR S_IWRITE      /* Write by owner.  */
#define S_IXUSR S_IEXEC       /* Execute by owner.  */
/* Read, write, and execute by owner.  */
#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC)

#define S_IRGRP (S_IRUSR >> 3)  /* Read by group.  */
#define S_IWGRP (S_IWUSR >> 3)  /* Write by group.  */
#define S_IXGRP (S_IXUSR >> 3)  /* Execute by group.  */
/* Read, write, and execute by group.  */
#define S_IRWXG (S_IRWXU >> 3)

#define S_IROTH (S_IRGRP >> 3)  /* Read by others.  */
#define S_IWOTH (S_IWGRP >> 3)  /* Write by others.  */
#define S_IXOTH (S_IXGRP >> 3)  /* Execute by others.  */
/* Read, write, and execute by others.  */
#define S_IRWXO (S_IRWXG >> 3)

在Windows下面只定义了上面的一部分,其中上面数值常量是8进制,这里是16进制:

#define _S_IFMT         0xF000          /* file type mask */
#define _S_IFDIR        0x4000          /* directory */
#define _S_IFCHR        0x2000          /* character special */
#define _S_IFIFO        0x1000          /* pipe */
#define _S_IFREG        0x8000          /* regular */
#define _S_IREAD        0x0100          /* read permission, owner */
#define _S_IWRITE       0x0080          /* write permission, owner */
#define _S_IEXEC        0x0040          /* execute/search permission, owner */

st_mode是stat最常用的字段,其中Linux下sys/stat.h中还定义了如下判断文件类型的宏:

/* Test macros for file types.  */

#define __S_ISTYPE(mode, mask)  (((mode) & __S_IFMT) == (mask))

#define S_ISDIR(mode)    __S_ISTYPE((mode), __S_IFDIR)
#define S_ISCHR(mode)    __S_ISTYPE((mode), __S_IFCHR)
#define S_ISBLK(mode)    __S_ISTYPE((mode), __S_IFBLK)
#define S_ISREG(mode)    __S_ISTYPE((mode), __S_IFREG)
#ifdef __S_IFIFO
# define S_ISFIFO(mode)  __S_ISTYPE((mode), __S_IFIFO)
#endif
#ifdef __S_IFLNK
# define S_ISLNK(mode)   __S_ISTYPE((mode), __S_IFLNK)
#endif

#if defined __USE_BSD && !defined __S_IFLNK
# define S_ISLNK(mode)  0
#endif

#if (defined __USE_BSD || defined __USE_UNIX98 || defined __USE_XOPEN2K) && defined __S_IFSOCK
# define S_ISSOCK(mode) __S_ISTYPE((mode), __S_IFSOCK)
#elif defined __USE_XOPEN2K
# define S_ISSOCK(mode) 0
#endif

2 stat函数

在linux的<sys/stat.h>文件中存在如下的三个函数,这三个函数都是用于查看一个文件的文件的属性信息,其中stat()和fstat()函数在Windows的头文件也进行了定义:

#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
				//成功返回0, 出错返回-1

stat函数返回一个文件的属性信息,fstat返回一个已在fd文件描述符打开的文件的属性信息。lstat是Linux下特有的,返回符合链接文件的信息,而不是所链接文件的信息。

下面一段文字是摘自维基百科关于文件描述符的介绍:”文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。”

下面一段代码是对stat的测试,在Windows和Linux下都可以运行:

#include <iostream>
#include <string>
#include <ctime>

#include <sys/stat.h>

using namespace std;

void printStat(struct stat *statBuf)
{
	if(statBuf == NULL)
		return;

	cout<<"stat.st_dev:"<<statBuf->st_dev<<endl;
	cout<<"stat.st_ino:"<<statBuf->st_ino<<endl;
	cout<<"stat.st_mode:"<<statBuf->st_mode<<endl;
	cout<<"stat.st_nlink:"<<statBuf->st_nlink<<endl;
	cout<<"stat.st_uid:"<<statBuf->st_uid<<endl;
	cout<<"stat.st_gid:"<<statBuf->st_gid<<endl;
	cout<<"stat.st_rdev:"<<statBuf->st_rdev<<endl;
	cout<<"stat.st_size:"<<statBuf->st_size<<endl;
	cout<<"stat.st_atime:"<<ctime(&statBuf->st_atime);
	cout<<"stat.st_ctime:"<<ctime(&statBuf->st_ctime);
	cout<<"stat.st_mtime:"<<ctime(&statBuf->st_mtime);
}

int main()
{
	struct stat fileInfoBuf;
	string fileName;

	while(1)
	{
		cout<<"please input the file path:";
		cin>>fileName;

		if(stat(fileName.c_str(), &fileInfoBuf) < 0)
		{
			cout<<"cann't get the information of file "<<fileName<<endl;
			continue;
		}
	
		printStat(&fileInfoBuf);
	}

	return 0;
}

3 Linux目录读取

目录的读取操作设计到具体的系统实现,C和C++都没有这方面的标准库。Windows和Linux下对目录的操作都不一样。Linux下面提供了下面的函数来进行目录的读取操作,其中前面三个是最常用的:

#include <dirent.h>
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dirp);
			//成功返回指针,失败返回NULL
int closedir(DIR *dirp);
			//成功返回0,失败返回-1
void rewinddir(DIR *dirp);
void seekdir(DIR *dirp, long offset);
long telldir(DIR *dirp);
			//成功返回当前条目的位置,失败返回-1

opendir()用于打开一个目录,如果打开成功则会返回一个DIR指针,该指针指向已打开的目录流。该目录流被定位到该目录的第一个条目。如果失败这返回NULL。

readdir()用于读取已打开目录中的条目。如果读取成功则返回一个dirent结构指针,该结构保存的是目录流所定位的目录中第n个条目的内容信息,并使DIR指针指向下一个目录条目。如果目录读取完毕或者出错返回NULL。

rewinddir()用于将DIR指针重新定位到目录的第一个条目。

seekdir()用于设置DIR所指向的目录流的位置,以提供给readdir()使用。Offset参数一般是根据telldir()获得的。

telldir()返回当前DIR指向目录流所在的位置,该位置是相对于目录的第一个条目而言的。

closedir()用于关闭DIR指针所关联的目录流,即关闭打开的目录。执行该函数后DIR所指向的结构将失效。

dirent的结构定义如下:

#include <bits/dirent.h>
struct dirent {
	ino_t d_ino;	/* inode number */
	off_t d_off;	/* offset to the next dirent */
	unsigned short d_reclen; /* length of this record */
	unsigned char  d_type;   /* type of file; not supported by all file system types */
	char d_name[256];	 /* filename */
};

其中d_inod_name字段是POSIX标准所规定必须定义的字段。其中d_type字段的取值有如下:

#include <dirent.h> 
DT_BLK      This is a block device.
DT_CHR      This is a character device.
DT_DIR      This is a directory.
DT_FIFO     This is a named pipe (FIFO).
DT_LNK      This is a symbolic link.
DT_REG      This is a regular file.
DT_SOCK     This is a Unix domain socket.
DT_UNKNOWN  The file type is unknown.

下面是测试代码,遍历指定目录下的文件名:

#include <iostream>
#include <cstring>

#include <dirent.h>

using namespace std;

void FileHandle(string filePath)
{
	cout<<filePath<<endl;
}

int DirProcess(string dirPath)
{
	DIR *pDir;
	struct dirent *pDirent;

	pDir = opendir(dirPath.c_str());
	if(pDir == NULL)
	{
		cout<<dirPath<<" is not directory..."<<endl;
		return -1;
	}

	while((pDirent = readdir(pDir)) != NULL)
	{
		//ignore the hidden directory "." & ".."
		if(strcmp(pDirent->d_name, ".") == 0 || strcmp(pDirent->d_name, "..") == 0)
			continue;

		//judge the directory entry is a directory or not
		if(pDirent->d_type == DT_DIR)
		{//recursive process the directory
			DirProcess(dirPath + '/' + pDirent->d_name);
		}
		else
		{
			 FileHandle(dirPath + '/' + pDirent->d_name);
		}
	}

	closedir(pDir);

	return 0;
}

int main(int argc, char **argv)
{
	if(argc == 1)
	{
		cout<<".:"<<endl;
		DirProcess(string("."));
	}
	else
	{
		for(int i = 1; i < argc; ++i)
		{
			cout<<argv[i]<<":"<<endl;
			DirProcess(string(argv[i]));
		}
	}
}

4 Windows目录读取

Windows下面的目录读取,底层的操作需要用到下面三个函数:

#include <io.h>
long _findfirst( char *filespec, struct _finddata_t *fileinfo );
int _findnext(long handle, struct _finddata_t *fileinfo);
int _findclose(long handle);

下面先介绍需要使用到的数据结构_finddata_t,该数据结构用于存储文件的相关信息。它的定义如下:

#include <io.h>
struct _finddata_t {
        unsigned    attrib; /*文件的属性字段,下面详细介绍*/
        __time32_t  time_create;	/* -1 for FAT file systems */
        __time32_t  time_access;	/* -1 for FAT file systems */
        __time32_t  time_write;
        _fsize_t    size;		/*文件的大小*/
        char        name[260];		/*文件名*/
};

其中attrib字段用于标识文件的属性,用位来进行标识,具体的值对应的属性如下:

/* File attribute constants for _findfirst() */
#define _A_NORMAL       0x00    /* Normal file - No read/write restrictions */
#define _A_RDONLY       0x01    /* Read only file */
#define _A_HIDDEN       0x02    /* Hidden file */
#define _A_SYSTEM       0x04    /* System file */
#define _A_SUBDIR       0x10    /* Subdirectory */
#define _A_ARCH         0x20    /* Archive file */

可以看出来,_finddata_t数据结构的元素除了name字段外,在stat数据结构中都存在。_finddata_t结构相对简单,是专门用来进行文件搜索使用的数据结构,匹配文件搜索的函数使用(_findfirst, _findnext, _findclose)。

下面将详细介绍上面三个函数:

_findfirst()用于搜索与指定的文件名称匹配的第一个实例,若成功则返回第一个实例的句柄,否则返回-1L。文件名filespec可以包含通配符:’*’(任意多个字符)和’?’(任意一个字符)。使用方式可以如下:

struct _finddata_t fileInfo;
_findfirst(“./*”), &fileInfo)) 		//搜索当前目录下的所有文件
_findfirst(“./*.txt”), &fileInfo)) 	//搜索当前目录下的所有txt文件

_findnext()用于搜索与_findfirst()函数提供的文件名称匹配的下一个实例,若成功则返回0,否则返回-1。

_findclose()用于关闭指定搜索句柄并释放关联的资源。

下面是测试代码,遍历指定目录下的文件名:

#include <iostream>
#include <cstring>
#include <string>
#include <vector>

#include <io.h>

using namespace std;

void FileHandle(string filePath)
{
	cout<<filePath<<endl;
}

int DirProcess(string dirPath)
{
	long handle;
	struct _finddata_t fileInfo;
	
	dirPath += "/*";
	
	if ((handle = _findfirst(dirPath.c_str(), &fileInfo)) == -1)
	{
		cout<<dirPath<<" is not directory..."<<endl;
		return -1;
	}

	do 
	{
		//ignore the hidden directory "." & ".."
		if(strcmp(fileInfo.name, ".") == 0 || strcmp(fileInfo.name, "..") == 0)
			continue;

		if (fileInfo.attrib & _A_SUBDIR)
		{//is a directory
			DirProcess(string(fileInfo.name));
		}
		else
		{//is a normal file
			FileHandle(string(fileInfo.name));
		}
	} while (_findnext(handle, &fileInfo) == 0);

	_findclose(handle);

	return 0;
}

int main(int argc, char **argv)
{
	if(argc == 1)
	{
		cout<<".:"<<endl;
		DirProcess(string("."));
	}
	else
	{
		for(int i = 1; i < argc; ++i)
		{
			cout<<argv[i]<<":"<<endl;
			DirProcess(string(argv[i]));
		}
	}

	system("pause");
}

5 低级文件操作

Windows下面<io.h>文件中定义了一些低级的文件操作和I/O函数。这些函数都是符合POSIX标准的,所以和Linux的同名函数功能上都是相似的。下面是<io.h>中的主要定义的函数:

int access(const char * _Filename, int _AccessMode);
int chmod(const char * _Filename, int _AccessMode);
int chsize(int _FileHandle, long _Size);
int close(int _FileHandle);
int creat(const char * _Filename, int _PermissionMode);
int dup(int _FileHandle);
int dup2(int _FileHandleSrc, int _FileHandleDst);
int eof(int _FileHandle);
long filelength( int _FileHandle);
int isatty(int _FileHandle);
int locking(int _FileHandle, int _LockMode, long _NumOfBytes);
long lseek(int _FileHandle, long _Offset, int _Origin);
char * mktemp(char * _TemplateName);
int open(const char * _Filename, int _OpenFlag, ...);
int read(int _FileHandle, void * _DstBuf, unsigned int _MaxCharCount);
int setmode(int _FileHandle, int _Mode);
int sopen(const char * _Filename,int _OpenFlag, int _ShareFlag, ...);
long tell(int _FileHandle);
int umask(int _Mode);
int write(int _Filehandle, const void * _Buf, unsigned int _MaxCharCount);

Create Time:Nov. 30, 2013

Last Modified Date:Nov. 30, 2013

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值