1. 程式人生 > >Windows平臺C語言獲取檔案的一些屬性

Windows平臺C語言獲取檔案的一些屬性

         Windows平臺有一個WIN32_FIND_DATA結構,用來儲存檔案的一些屬性(這裡指的屬性和下面結構中檔案屬性成員不同。這裡的屬性是指下面結構的所有成員)。

該結構的定義如下。

typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //檔案屬性
FILETIME ftCreationTime; // 檔案建立時間
FILETIME ftLastAccessTime; // 檔案最後一次訪問時間
FILETIME ftLastWriteTime; // 檔案最後一次修改時間
DWORD nFileSizeHigh; // 檔案長度高32位
DWORD nFileSizeLow; // 檔案長度低32位
DWORD dwReserved0; // 系統保留
DWORD dwReserved1; // 系統保留
TCHAR cFileName[ MAX_PATH ]; // 長檔名
TCHAR cAlternateFileName[ 14 ]; // 8.3格式檔名
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;

遍歷檔案:

        可以使用FindFirstFile()和 FindNextFile()函式可以得到 某個資料夾裡面所有的檔案(包括子資料夾)的WIN32_FIND_DATA結構資訊。

        FindFirstFile的原型如下:

HANDLE FindFirstFile(
   LPCTSTR lpFileName,
   LPWIN32_FIND_DATA lpFindFileData
);

        FindFirstFile()函式中,第一個引數是一個字串。可以是一個路徑名或者檔名,並且支援萬用字元 * 和 ?。比如想查詢D盤下的所有檔案,可以寫成D:\\*.* 或者 D:\\*。

        如果只想查詢D盤下的txt檔案,那麼可以寫成D:\\*.txt。

        第二個引數是指向WIN32_FIND_DATA結構體的一個指標。正如函式的名字FindFirstFile那樣, 該函式會查詢第一個符合查詢條件的檔案(使用萬用字元可以有多少檔案滿足查詢條件)。然後把這個檔案的一些資訊寫入這個結構裡面。如果第一個引數沒有使用萬用字元,而是一個檔名,那麼將只能找到一個符合條件的檔案。

        函式的返回值是一個控制代碼HANDLE,說白了也就是一個整型。這個返回值可以用來查詢下一個符合查詢條件的檔案。這就是下面的FindNextFile函數了。如果函式呼叫失敗,將返回INVALID_HANDLE_VALUE

        FindNextFile的原型如下

BOOL FindNextFile(
  HANDLE hFindFile,
  LPWIN32_FIND_DATA lpFindFileData
);

        第一個引數就是FindFirstFile函式的返回值。第二個引數和FindFirstFile一樣,在一個指標,用來存放被查詢到的檔案的一些資訊。

        如果查詢成功,函式返回非0值。否則返回0。可以呼叫GetLastError()函式來檢視失敗原因。如果沒有符合要求的檔案了,那麼也將返回0。此時,呼叫GetLastError()函式將返回ERROR_NO_MORE_FILES。

        在使用完上面兩個函式後,要記得使用CloseFile(HANDLEhFindFile)函式來關閉這個控制代碼。

        上面三個函式需要包括windows.h標頭檔案.

        說了這麼多,下面給出一個例子程式碼。

#include<iostream>
#include<windows.h>
using namespace std;

int main()
{
	WIN32_FIND_DATA  fileAttr;
	HANDLE  handle;
	handle = FindFirstFile("D:\\*", &fileAttr);

	if( handle == INVALID_HANDLE_VALUE ) 
	{
		cout<<"invalid handle value "<<GetLastError()<<endl;
	}
	else
	{
		cout<<fileAttr.cFileName<<endl; //輸出查詢到的檔名

		while(  FindNextFile(handle, &fileAttr)  )
		{
			cout<<fileAttr.cFileName<<endl; //輸出每一個查詢到的檔名
		}

		if( GetLastError() == ERROR_NO_MORE_FILES )
		{
			cout<<"查詢完畢"<<endl;
		}
		else 
		{
			cout<<"查詢過程出現錯誤"<<endl;
		}

		FindClose(handle);
	}

	return 0;
}

        上面的程式會遍歷D盤下面的所有檔案和資料夾。包括當前目錄和父目錄,這兩個目錄對應的檔名為.和..,即一個點和兩個點。熟悉Linux的讀者就很容易明白。

        也可以查詢相對路徑的檔案,比如"*.txt",就查詢當前目錄下的所有txt檔案

        如果在編譯的時候出現了cannot convert 'const char*' to 'LPCWSTR' 錯誤,可以把WIN32_FIND_DATA換成WIN32_FIND_DATAA,即在最後追加一個A,FindFirstFile和FindNextFile也要追加一個A。

檔案屬性:

        現在看一下WIN32_FIND_DATA結構的一些成員資訊。

大小屬性:

        首先看一下檔案的大小資訊。該資訊用了兩個成員儲存。分別是:

        DWORD  nFileSizeHigh;// 檔案長度高32位

        DWORD  nFileSizeLow;// 檔案長度低32位

        其中,單位是 位元組數。

        由於nFileSizeHigh儲存的是檔案長度的高位,所以當檔案的大小小於MAXDWORD時,該成員的值為0。最後,可以用 (nFileSizeHigh * (MAXDWORD+1)) +  nFileSizeLow 來計算檔案大小。

資料夾的大小是 0。

檔案型別屬性:

        檔案型別屬性由dwFileAttributes成員指明。最常用的型別當然就是:普通檔案和資料夾,分別對應FILE_ATTRIBUTE_ARCHIVE和FILE_ATTRIBUTE_DIRECTORY。更多的屬性可以參照這裡。因為一個檔案可能包含多個屬性,所以判斷的時候使用諸如:FILE_ATTRIBUTE_ARCHIVE & dwFileAttributes。而不是使用==符號。

時間屬性:

        現在看一下與檔案有關的時間屬性。有三個時間,建立時間、訪問時間、修改時間。三者都差不多。只需弄懂一個,其他兩個就自然知道怎麼用了。

        從文章最前面的WIN32_FIND_DATA結構體可以看到其ftCreationTime 成員是一個FILETIME型別。宣告如下:

typedef struct _FILETIME {
  DWORD dwLowDateTime;
  DWORD dwHighDateTime;
} FILETIME, *PFILETIME;

        一眼看過去,都不知道怎麼使用。就像C語言標準庫裡面的time()函式一樣,返回一個從1970年到現在的秒數。這個值很難使用。還好Windows還提供了另外一個結構 SYSTEMTIME。其宣告如下:

typedef struct _SYSTEMTIME {
  WORD wYear;
  WORD wMonth;
  WORD wDayOfWeek;
  WORD wDay;
  WORD wHour;
  WORD wMinute;
  WORD wSecond;
  WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;

        這個結構看起來才像人使用的,剛才那個完全是機器使用的。

        同樣,Windows提供了兩者相互轉換的函式.

BOOL FileTimeToSystemTime(
  const FILETIME *lpFileTime,
  LPSYSTEMTIME lpSystemTime
);
BOOL SystemTimeToFileTime(
  const SYSTEMTIME *lpSystemTime,
  LPFILETIME lpFileTime
);

兩個函式都需要對應結構體的指標。

        還有一個東西需要注意。

        通過FindFirstFile、FindNextFile獲取的WIN32_FIND_DATA結構。其FILETIME成員的值都是使用UTC時間,就是中學地理學的格林尼治時間。我們中國用東8區時間。所以我們還要在這些成員值的基礎上加上 8 小時。微軟不推薦我們直接對FILETIME結構進行 加上 或者減少 某個時間。如果我們要轉換成我們當地的時間(就是從UTC時間轉換成東8區時間),可以使用函式 FileTimeToLocalFileTime。其宣告如下:
BOOL FileTimeToLocalFileTime(
  const FILETIME *lpFileTime,
  LPFILETIME lpLocalFileTime
);

         另外,不能就地修改。就是說,第一個引數和第二個引數要指向不同的記憶體。

         給一個例子吧。

#include<iostream>
#include<windows.h>
using namespace std;

ostream& operator << (ostream& os, const SYSTEMTIME& t)
{
	os<<t.wYear<<"-"<<t.wMonth<<"-"<<t.wDay<<"  ";
	os<<t.wHour<<": "<<t.wMinute<<": "<<t.wSecond<<endl;
	return os;
}

int main()
{
	
	WIN32_FIND_DATA fileAttr;
	HANDLE handle;
	
	SYSTEMTIME sysTime;
	FILETIME localFileTime;
	
	handle = FindFirstFile("D:\\*.txt", &fileAttr);
	
	if( handle == INVALID_HANDLE_VALUE )
	{
		cout<<"invalid handle value "<<GetLastError()<<endl;
	}
	else
	{
		cout<<fileAttr.cFileName<<" size is "<<((fileAttr.nFileSizeHigh * (MAXDWORD+1))  +  fileAttr.nFileSizeLow) <<endl;
		FileTimeToLocalFileTime(&fileAttr.ftCreationTime, &localFileTime); //轉換成當地時間
		
		FileTimeToSystemTime(&localFileTime, &sysTime); //轉換成人看的時間型別
		cout<<sysTime<<endl;
		
		FindClose(handle);
	}
	
	return 0;
}