上一篇介紹了一種常見的文件處理方法(可優化為:分次讀取文件,但要滿足根據行號能快速索引該行內容時會遇到麻煩),所以此片我將介紹另一種更高效,實用,并對本進程的內存空間地址消耗小的方法!
一. 預備知識
1). Windows編程處理文件的相關接口:
A)CreateFile();
B)ReadFile();
c)WriteFile();
d)CloseHandle();
2). 文件映射相關接口:
A) ?CreateFileMapping()
B)MapFIleOfView()
C)OpenFileMapping()
D)CloseHandle()
3)注:
由于以上接口MSDN以及網上的牛人們已經給了詳細的介紹了,這里我只累敘 一下MapViewOfFile()第四個參數 DWORD dwFileOffsetLow;
此偏移量必須64k對齊,可能是由于便于內存管理得快速訪問,才做此限定,如下代碼:
LARGE_INTEGER liOffset = {};liOffset.QuadPart = lpLineInfo->liHeadCharOffset.QuadPart & (~MemmoryHex_64k);DWORD DValue = DWORD(lpLineInfo->liHeadCharOffset.QuadPart - liOffset.QuadPart);char *lpHeadSrc = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, liOffset.HighPart, liOffset.LowPart, (DValue + lpLineInfo->LineCount));
二.思想
1) 在處理文件過程中,我們一般采用的方法是分塊(如每次512k)讀取文件數據進行處理,這樣做的目的如下
A)申請小塊內存速度快
B)可以不用New動態創建存數據的Buffer,而是直接可以用棧存儲次Buffer,降低內存泄漏的風險
C)若一次申請一塊較大的地址空間(>512M),那你還讓其他線程工作嗎?一個進程中的多個線程是共用同一塊地址空間的!
對于B:有朋友會說,那可以將進程中允許訪問的棧空間調大(1G),但這樣是有風險的,比如你在編寫遞歸代碼時出錯,可能導致死循環,由于堆設得過大,導致要許久才會導致棧溢出,增加我們調式bug得難度!而當棧的空間地址范圍較小時,函數調用棧很快就會溢出,Buf易復現,就便于修改了!
2)當文件大小在允許訪問內(我的機器<24G),將文件內容映射到內存上,對文件的訪問速率相當于內存的讀取率;
A)注:我的公司開發機:Win7 64位,16G運行內存,15G虛擬內存)
B)由于映射的內存塊可以被本機的其他進程訪問,所以我們通常也稱之為共享內存!
3)每次映射小塊文件數據到次進程的地址空間上,進行處理(與上篇中處理方式一樣)
DWORD RelMapMemorySize = DWORD((liTotalFileSize.QuadPart - liHandleFileSize.QuadPart + DValue > EachMapMemorySzie) ? EachMapMemorySzie : (liTotalFileSize.QuadPart - liHandleFileSize.QuadPart + DValue));lpHeadSrc = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, liOffset.HighPart, liOffset.LowPart, RelMapMemorySize);
4)根據行號索引該行內容,相當于隨機訪問數組的速率
相交與上篇,此篇存儲每一行信息的數據結構有所改變,如下
typedef struct LINE_INFO{LARGE_INTEGER liHeadCharOffset;INT32 LineCount;}LINE_INFO;
A)我們根據liHeadCharOffset算出映射時候的偏移量!
B)注意:我們必須保證此低字節的偏移量必須64K對其,所以這里必須先對liHeadCharOffset進行處理!
a)映射偏移量首先對此值取商
LARGE_INTEGER liOffset = {};liOffset.QuadPart = lpLineInfo->liHeadCharOffset.QuadPart & (~MemmoryHex_64k);DWORD DValue = DWORD(lpLineInfo->liHeadCharOffset.QuadPart - liOffset.QuadPart);
b)處理時再加上我們做商時舍棄的差值
char *lpHeadSrc = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, liOffset.HighPart, liOffset.LowPart, (DValue + lpLineInfo->LineCount));if (NULL == lpHeadSrc){m_lpHLog->TraceLog("Error(%s.%d): MapViewOfFile Failed!\r\n", __FUNCTION__, __LINE__);return FALSE;}char *lpHead = lpHeadSrc + DValue;