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