使用文件設(shè)備
因?yàn)槲募氖褂梅浅F毡?,我將花一些時(shí)間來專門討論適用于文件設(shè)備的一些要點(diǎn)。本節(jié)討論了如何定位文件指針和改變文件大小。
你必須明白的第一個(gè)要點(diǎn)是Windows被設(shè)計(jì)為可操作非常大的文件。微軟的設(shè)計(jì)師們?cè)揪瓦x用了64-bit的數(shù)值來描述一個(gè)文件的大小,而不是32-bit。這就意味著一個(gè)文件理論上可達(dá)到16EB(exabytes)。
在一個(gè)32-bit的操作系統(tǒng)上處理64-bit的數(shù)值,使得操作文件有點(diǎn)令人不快,因?yàn)樵S多Windows函數(shù)要求你以兩個(gè)獨(dú)立的32-bit數(shù)值來傳遞64-bit參數(shù)。不過你會(huì)發(fā)現(xiàn),使用這些參數(shù)并不是很困難,而且在日常操作中,你不太可能會(huì)使用一個(gè)大于4GB的文件。也就是說表示一個(gè)文件大小的 64-bit數(shù)值的高32-bit通??偸菫?。
取得文件大小
當(dāng)你使用文件時(shí),經(jīng)常需要獲取文件的大小。最簡單的方法是調(diào)用GetFileSizeEx:
BOOL GetFileSizeEx(
HANDLE hfile,
PLARGE_INTEGER pliFileSize);
第一個(gè)參數(shù)hfile是文件的句柄,pliFileSize參數(shù)是一個(gè)LARGE_INTEGER聯(lián)合體的地址。LARGE_INTEGER聯(lián)合使得一個(gè) 64-bit數(shù)既可以作為兩個(gè)32-bit的數(shù)值,也可以作為一個(gè)單獨(dú)的64-bit數(shù)值來引用,這在計(jì)算文件大小和偏移值時(shí)是相當(dāng)方便的。這個(gè)聯(lián)合看起來(基本上)如下所示:
typedef union _LARGE_INTEGER {
struct {
DWORD LowPart; // Low 32-bit unsigned value
LONG HighPart; // High 32-bit signed value
};
LONGLONG QuadPart; // Full 64-bit signed value
} LARGE_INTEGER, *PLARGE_INTEGER;
除了LARGE_INTEGER外,還有ULARGE_INTEGER聯(lián)合用來描述一個(gè)無符號(hào)的64-bit數(shù)值:
typedef union _ULARGE_INTEGER {
struct {
DWORD LowPart; // Low 32-bit unsigned value
DWORD HighPart; // High 32-bit unsigned value
};
ULONGLONG QuadPart; // Full 64-bit unsigned value
} ULARGE_INTEGER, *PULARGE_INTEGER;
另外一個(gè)很有用的獲取文件大小的函數(shù)是GetCompressedFileSize:
DWORD GetCompressedFileSize(
PCTSTR pszFileName,
PDWORD pdwFileSizeHigh);
這個(gè)函數(shù)返回文件的物理大小,而GetFileSizeEx返回的是文件的邏輯大小。例如,考慮一個(gè)100-KB的文件被壓縮后占用85 KB空間。調(diào)用GetFileSizeEx返回的是文件的邏輯大小100 KB,而GetCompressedFileSize返回的是文件在磁盤上實(shí)際占用的字節(jié)數(shù)85 KB。
與GetFileSizeEx不同的是,GetCompressedFileSize的第一個(gè)參數(shù)用一個(gè)文件名作為字符串傳遞而不是使用句柄。 GetCompressedFileSize函數(shù)以一種與眾不同的方式返回64-bit的數(shù)值:文件大小的低32-bit就是函數(shù)的返回值,高32- bit被放置于pdwFileSizeHigh參數(shù)指向的DWORD變量中。下面是ULARGE_INTEGER聯(lián)合常見的用法:
ULARGE_INTEGER ulFileSize;
ulFileSize.LowPart = GetCompressedFileSize("SomeFile.dat",
&ulFileSize.HighPart);
// 64-bit file size is now in ulFileSize.QuadPart
定位文件指針
調(diào)用CreateFile會(huì)使系統(tǒng)創(chuàng)建一個(gè)文件內(nèi)核對(duì)象來管理與文件相關(guān)的各種操作。這個(gè)內(nèi)核對(duì)象的內(nèi)幕是一個(gè)文件指針。文件指針就是一個(gè)文件內(nèi)的64- bit偏移值,指示下一次同步讀寫的位置。開始時(shí),文件指針被設(shè)為0,所以如果你在調(diào)用CreateFile后立即調(diào)用ReadFile,你將從文件的偏移0處開始讀。如果你從文件讀了10字節(jié)到內(nèi)存,系統(tǒng)會(huì)更新與文件句柄關(guān)聯(lián)的指針,使得下次調(diào)用ReadFile時(shí),從文件10字節(jié)處開始讀。例如,看一下這段代碼,其中文件的開始10字節(jié)被讀入緩存,接著又把隨后的10字節(jié)讀入緩存:
BYTE pb[10];
DWORD dwNumBytes;
HANDLE hfile = CreateFile("MyFile.dat", ...); // Pointer set to 0
ReadFile(hfile, pb, 10, &dwNumBytes, NULL); // Reads bytes 0 - 9
ReadFile(hfile, pb, 10, &dwNumBytes, NULL); // Reads bytes 10 - 19
因?yàn)槊恳粋€(gè)文件內(nèi)核對(duì)象有自己的文件指針,故兩次打開同一個(gè)文件會(huì)有稍微不同的結(jié)果:
BYTE pb[10];
DWORD dwNumBytes;
HANDLE hfile1 = CreateFile("MyFile.dat", ...); // Pointer set to 0
HANDLE hfile2 = CreateFile("MyFile.dat", ...); // Pointer set to 0
ReadFile(hfile1, pb, 10, &dwNumBytes, NULL); // Reads bytes 0 - 9
ReadFile(hfile2, pb, 10, &dwNumBytes, NULL); // Reads bytes 0 - 9
在這個(gè)例子中,兩個(gè)不同的內(nèi)核對(duì)象管理同一個(gè)文件。由于每個(gè)內(nèi)核對(duì)象都有自己的文件指針,用一個(gè)文件對(duì)象操作文件時(shí)不會(huì)影響另一個(gè)對(duì)象維護(hù)的文件指針,故文件開始處的10字節(jié)會(huì)被讀兩次。
我想多舉一個(gè)例子以使得這些內(nèi)容更清晰:
BYTE pb[10];
DWORD dwNumBytes;
HANDLE hfile1 = CreateFile("MyFile.dat", ...); // Pointer set to 0
HANDLE hfile2;
DuplicateHandle(
GetCurrentProcess(), hfile1,
GetCurrentProcess(), &hfile2,
0, FALSE, DUPLICATE_SAME_ACCESS);
ReadFile(hfile1, pb, 10, &dwNumBytes, NULL); // Reads bytes 0 - 9
ReadFile(hfile2, pb, 10, &dwNumBytes, NULL); // Reads bytes 10 - 19
在這個(gè)例子中,一個(gè)文件內(nèi)核對(duì)象被兩個(gè)文件句柄引用。不管用哪一個(gè)句柄操作文件,都只有一個(gè)文件指針被更新。和上個(gè)例子一樣,每次都讀出不同的字節(jié)。
如果你需要隨機(jī)訪問一個(gè)文件,你就需要改變與文件內(nèi)核對(duì)象相關(guān)聯(lián)的文件指針。調(diào)用SetFilePointerEx可以做到這一點(diǎn):
BOOL SetFilePointerEx(
HANDLE hfile,
LARGE_INTEGER liDistanceToMove,
PLARGE_INTEGER pliNewFilePointer,
DWORD dwMoveMethod);
hfile參數(shù)指示你想改變其文件指針的文件內(nèi)核對(duì)象。iDistanceToMove參數(shù)告訴系統(tǒng)你想移動(dòng)指針多少個(gè)字節(jié)。你指定的數(shù)字是增加到文件指針的當(dāng)前值,所以一個(gè)負(fù)數(shù)將引起文件指針的倒退。SetFilePointerEx的最后一個(gè)參數(shù),dwMoveMethod,告訴 SetFilePointerEx如何解釋liDistanceToMove參數(shù)。表 2-8 描述了你可以傳遞給dwMoveMethod的三個(gè)不同值以指定移動(dòng)的起始點(diǎn)。
表 2-8.可傳遞給SetFilePointerEx的dwMoveMethod參數(shù)的值
參數(shù)值 含義
FILE_BEGIN 文件對(duì)象的文件指針被設(shè)為liDistanceToMove參數(shù)給出的值。注意liDistanceToMove被當(dāng)成一個(gè)無符號(hào)的64-bit值。
FILE_CURRENT 文件對(duì)象的文件指針的值被加上liDistanceToMove。注意liDistanceToMove被當(dāng)成一個(gè)有符號(hào)的64-bit值,你可以在文件中向后移動(dòng)指針。
FILE_END 文件對(duì)象的文件指針被設(shè)為文件邏輯大小加上liDistanceToMove的值。注意liDistanceToMove被當(dāng)成一個(gè)有符號(hào)的64-bit值,你可以在文件中向后移動(dòng)指針。
在SetFilePointerEx更新文件對(duì)象的文件指針后,文件指針的新值被返回到由pliNewFilePointer參數(shù)指向的LARGE_INTEGER。如果你對(duì)新指針值不感興趣,可以傳遞NULL給pliNewFilePointer。
關(guān)于SetFilePointerEx這里給出一些要注意的要點(diǎn):
■ 設(shè)置文件指針越過文件的當(dāng)前長度末尾是合法的。這樣做并不會(huì)實(shí)際增加文件在磁盤上的大小,除非你在這個(gè)位置寫文件或調(diào)用SetEndOfFile。
■ 對(duì)由FILE_FLAG_NO_BUFFERING標(biāo)志打開的文件使用SetFilePointerEx時(shí),文件指針只能被定位于對(duì)齊扇區(qū)的邊界處。本章后面的FileCopy示例程序演示了如何正確使用這種方式。
■ Windows沒有提供一個(gè)GetFilePointerEx函數(shù),但是你可以使用SetFilePointerEx來獲得期望的效果:
LARGE_INTEGER liCurrentPosition = { 0 };
SetFilePointerEx(hfile, liCurrentPosition, &liCurrentPosition,
FILE_CURRENT);
設(shè)置文件尾
通常,系統(tǒng)在文件關(guān)閉時(shí)負(fù)責(zé)設(shè)置文件尾。不過,你可能有時(shí)想要使一個(gè)文件變大或變小。在這些情況下,調(diào)用
BOOL SetEndOfFile(HANDLE hfile);
SetEndOfFile截?cái)嗷蜓由煲粋€(gè)文件的末尾到文件對(duì)象的指針?biāo)甘镜奈恢?。例如,如果你想?qiáng)制一個(gè)文件為1024字節(jié)長,你可以這樣使用SetEndOfFile:
HANDLE hfile = CreateFile(...);
LARGE_INTEGER liDistanceToMove;
liDistanceToMove.QuadPart = 1024;
SetFilePointerEx(hfile, liDistanceToMove, NULL, FILE_BEGIN);
SetEndOfFile(hfile);
CloseHandle(hfile);
用Windows Explorer查看該文件的屬性,你會(huì)看到文件正好是1024字節(jié)長。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)
點(diǎn)擊舉報(bào)。