1. 程式人生 > >Linux和Windows下檔案和目錄的相關屬性及操作

Linux和Windows下檔案和目錄的相關屬性及操作

我們知道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下面:

[[email protected] ~]$ 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()用於搜尋與函式提供的檔名稱匹配的下一個例項,若成功則返回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