天天看點

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;
}