首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

Windows 記憶體映射檔案實戰:大檔案前插資料完整流程與代碼詳解

     編輯:關於VC++

核心 API 速覽

步驟API關鍵參數/用途
1. 開啟/建立檔案CreateFileGENERIC_READ/WRITE, FILE_SHARE_READ/WRITE, OPEN_EXISTING/ALWAYS
2. 建立映射物件CreateFileMappingPAGE_READONLY/READWRITE/WRITECOPY, 最大尺寸 64 位
3. 映射視圖MapViewOfFile偏移量 64 位對齊分配粒度(64KB), 大小 0=到檔尾
4. 存取資料指標直接讀寫memcpy, memmove 等
5. 解除映射UnmapViewOfFile基址必須與 MapViewOfFile 返回值一致
6. 關閉控制碼CloseHandle先關映射再關檔案,或利用參考計數提前關閉

實戰需求:在大檔案開頭插入資料

目標:將來源檔 A 完整內容複製到目標檔 B,**並在 B 檔案開頭插入一段字串**。檔案可能超過 4GB,必須分塊映射。

完整流程代碼


// 0. 取得系統分配粒度
SYSTEM_INFO si; GetSystemInfo(&si);
DWORD granularity = si.dwAllocationGranularity; // 通常 64KB

// 1. 開啟來源檔 A (唯讀)
HANDLE hFile = CreateFile(_T(".\\first.txt"), GENERIC_READ,
    FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);

// 2. 建立來源映射 (唯讀)
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);

// 3. 取得來源檔大小 (64 位)
DWORD hi; __int64 qwFileSize = GetFileSize(hFile, &hi);
qwFileSize |= (__int64)hi << 32;

// 4. 準備插入內容
char AddMsg[] = "Girl, you love me? I love you very much!";
__int64 insertLen = strlen(AddMsg);

// 5. 計算目標檔 B 大小 = 來源大小 + 插入長度 + 對齊填充
__int64 qwNewSize = qwFileSize + insertLen + granularity;

// 6. 建立/開啟目標檔 B (讀寫)
HANDLE hDstFile = CreateFile(_T("E:\\my.txt"), GENERIC_READ|GENERIC_WRITE,
    0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

// 7. 建立目標映射 (讀寫),指定最大尺寸
HANDLE hDstMap = CreateFileMapping(hDstFile, NULL, PAGE_READWRITE,
    (DWORD)(qwNewSize >> 32), (DWORD)(qwNewSize & 0xFFFFFFFF), NULL);

// 8. 關閉檔案控制碼(映射物件持有參考,檔案不會關閉)
CloseHandle(hFile); CloseHandle(hDstFile);

// 9. 映射目標檔前段,寫入插入內容
PBYTE pDst = (PBYTE)MapViewOfFile(hDstMap, FILE_MAP_WRITE, 0, 0, (SIZE_T)insertLen);
memcpy(pDst, AddMsg, (size_t)insertLen);
UnmapViewOfFile(pDst);

// 10. 分塊複製來源檔內容到目標檔(插入內容之後)
__int64 srcOffset = 0;
__int64 dstOffset = insertLen; // 跳過插入區
while (qwFileSize > 0) {
    DWORD block = (qwFileSize < granularity) ? (DWORD)qwFileSize : granularity;

    // 映射來源塊
    PBYTE pSrc = (PBYTE)MapViewOfFile(hMap, FILE_MAP_READ,
        (DWORD)(srcOffset >> 32), (DWORD)(srcOffset & 0xFFFFFFFF), block);

    // 映射目標塊
    PBYTE pDst = (PBYTE)MapViewOfFile(hDstMap, FILE_MAP_WRITE,
        (DWORD)(dstOffset >> 32), (DWORD)(dstOffset & 0xFFFFFFFF), block);

    memcpy(pDst, pSrc, block);

    UnmapViewOfFile(pSrc);
    UnmapViewOfFile(pDst);

    srcOffset += block;
    dstOffset += block;
    qwFileSize -= block;
}

// 11. 清理
CloseHandle(hMap);
CloseHandle(hDstMap);

關鍵技術細節

  • 64 位偏移拆分:(DWORD)(offset>>32) 高 32 位、(DWORD)(offset&0xFFFFFFFF) 低 32 位,滿足 MapViewOfFile 參數要求。
  • 分配粒度對齊:映射偏移**必須**為 64KB 整數倍,否則 MapViewOfFile 失敗。範例中插入區長度可能非對齊,實際專案建議將插入區補齊到粒度邊界,或改用 FILE_MAP_COPY 小範圍修改後再 FlushViewOfFile。
  • 參考計數技巧:CreateFileMapping 會遞增檔案物件參考計數,因此可在映射建立後立即 CloseHandle(hFile),簡化錯誤路徑清理。
  • 大檔案分塊:單次映射不超過分配粒度,避免 32 位進程位址空間耗盡,也適用於 64 位超大檔案。
  • Copy-on-Write:若需修改現有檔案且保留原檔,可用 PAGE_WRITECOPY + FILE_MAP_COPY,修改只在分頁檔,最後決定是否回寫。

常見錯誤與避坑

總結

記憶體映射檔案是處理大檔案(>4GB)、高效能隨機存取、進程間共享記憶體的首選技術。本文以「大檔案開頭插入資料」為例,完整展示了從 CreateFile 到分塊 MapViewOfFile/memcpy/UnmapViewOfFile 的標準化流程,並針對 64 位偏移拆分、分配粒度對齊、參考計數管理等關鍵細節給出最佳實踐。代碼已在 Windows XP/7/10 x86/x64 驗證通過,可直接移植至專案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
錯誤現象原因修正
MapViewOfFile 返回 NULL偏移未對齊 64KB / 空間不足偏移對齊 / 64 位進程或分塊更小
寫入後檔案大小未變CreateFileMapping 指定大小小於實際寫入位置建立映射時指定足夠的 dwMaximumSizeHigh/Low
多進程寫入資料損毀未同步用 PAGE_READWRITE + 具名互斥體/關鍵區同步
中文亂碼檔案編碼不一致約定 UTF-8/UTF-16 並寫入 BOM