使用Windows文件映射分块处理>250KB文件时异或不完整求助
问题分析与修复方案
我帮你排查了代码里的几个关键问题,这就是为什么大文件处理不完全的原因:
核心错误:字节偏移与DWORD索引混淆
你的mapWriteChunk函数里,offset直接用了start值,但start(也就是pos)是DWORD的索引(比如pos=4表示第4个4字节块),而文件映射需要的是字节偏移量。这就导致大文件时,你实际处理的位置远小于预期,看起来只处理了一部分。
比如,当pos=250KB/4=64000时,你把offset设为64000字节(仅62.5KB),而不是64000*4=256000字节,这就漏掉了后面的内容。
其他关键问题
- 分块大小的单位错误:
buffsize是DWORD的数量,但文件映射的大小需要用字节,所以要乘以sizeof(DWORD)。 - MapViewOfFile的映射大小参数错误:你传了0,这会映射整个文件,分块处理的意义就没了,还可能导致效率问题,应该传计算好的
dwMapViewSize。 - uixor函数的冗余与错误:
- 每次调用都用
srand(seed)重置随机数生成器,导致每次生成的mask完全相同(因为相同seed的rand()第一个值固定)。 - 绕了大弯把unsigned int转成char数组再异或,完全没必要,直接对整数异或即可。
- 每次调用都用
- 文件大小非4字节倍数的处理遗漏:如果文件大小不是4的整数倍,最后几个字节会被忽略。
- 过时的OpenFile函数:建议用更现代可靠的
CreateFile代替。
修复后的完整代码
#include "stdafx.h" #include "Windows.h" #include <stdio.h> #include <stdint.h> #include <iso646.h> #include <math.h> #include <stdlib.h> // 简化异或逻辑:直接对unsigned int异或,无需转字符串 unsigned int uixor(unsigned int n, unsigned int seed) { static int isSeeded = 0; // 只初始化一次随机数生成器,避免每次重置导致mask相同 if (!isSeeded) { srand(seed); isSeeded = 1; } unsigned int mask = rand(); return n ^ mask; } BOOL mapWriteChunk(PHANDLE phFileMap, DWORD dwFileSize, DWORD dwordIndexStart, int dwordChunkSize, uint32_t xork) { // 把DWORD索引转成字节偏移 DWORD byteOffset = dwordIndexStart * sizeof(DWORD); // 计算当前分块的总字节数 DWORD chunkByteSize = dwordChunkSize * sizeof(DWORD); // 处理文件末尾的不完整块 if (byteOffset + chunkByteSize > dwFileSize) { chunkByteSize = dwFileSize - byteOffset; // 如果剩下的字节不足4,直接返回(或者你可以选择处理单个字节,根据需求调整) if (chunkByteSize < sizeof(DWORD)) { return TRUE; } // 调整为DWORD的整数倍 chunkByteSize -= chunkByteSize % sizeof(DWORD); } SYSTEM_INFO SysInfo; GetSystemInfo(&SysInfo); DWORD dwSysGran = SysInfo.dwAllocationGranularity; // 计算文件映射的起始位置(必须是分配粒度的整数倍) DWORD dwFileMapStart = (byteOffset / dwSysGran) * dwSysGran; // 计算视图的大小:从映射起始到当前分块结束的字节数 DWORD dwMapViewSize = byteOffset - dwFileMapStart + chunkByteSize; // 映射视图到内存 unsigned char* pViewBuffer = (unsigned char*)MapViewOfFile(*phFileMap, FILE_MAP_ALL_ACCESS, 0, dwFileMapStart, dwMapViewSize); if (pViewBuffer == NULL) { printf("MapViewOfFile failed, error: %lu\n", GetLastError()); return FALSE; } // 计算当前分块在视图中的偏移 int viewDelta = byteOffset - dwFileMapStart; unsigned int* pDwordBuffer = (unsigned int*)(pViewBuffer + viewDelta); // 逐个处理每个DWORD int dwordCount = chunkByteSize / sizeof(DWORD); for (int i = 0; i < dwordCount; i++) { unsigned int original = pDwordBuffer[i]; pDwordBuffer[i] = uixor(original, xork); // printf("Processed DWORD at index %d\n", dwordIndexStart + i); } // 取消映射 if (!UnmapViewOfFile(pViewBuffer)) { printf("UnmapViewOfFile failed, error: %lu\n", GetLastError()); return FALSE; } return TRUE; } int main() { const char* fileName = "test.txt"; // 用CreateFile代替过时的OpenFile HANDLE hFile = CreateFileA(fileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("CreateFile failed, error: %lu\n", GetLastError()); system("PAUSE"); return 1; } DWORD dwFileSize = GetFileSize(hFile, NULL); if (dwFileSize == INVALID_FILE_SIZE) { printf("GetFileSize failed, error: %lu\n", GetLastError()); CloseHandle(hFile); system("PAUSE"); return 1; } // 创建文件映射 HANDLE hFileMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, dwFileSize, NULL); if (hFileMap == NULL) { printf("CreateFileMapping failed, error: %lu\n", GetLastError()); CloseHandle(hFile); system("PAUSE"); return 1; } int chunkDwordCount = 4; // 每次处理4个DWORD(16字节) int totalDwordCount = dwFileSize / sizeof(DWORD); uint32_t rseed = 10; for (int pos = 0; pos < totalDwordCount; pos += chunkDwordCount) { if (!mapWriteChunk(&hFileMap, dwFileSize, pos, chunkDwordCount, rseed)) { printf("Failed to process chunk starting at DWORD index %d\n", pos); break; } } // 关闭句柄,注意顺序:先关闭映射,再关闭文件 CloseHandle(hFileMap); CloseHandle(hFile); system("PAUSE"); return 0; }
修复要点说明
- 字节偏移修正:把
DWORD索引 * sizeof(DWORD)转成实际的字节偏移,确保映射到正确的文件位置。 - 分块单位统一:所有大小计算都用字节,避免混淆DWORD数量和字节数。
- 随机数生成优化:
uixor里只初始化一次随机数生成器,保证每次生成的mask不同。 - 文件末尾处理:如果文件大小不是4的整数倍,处理到最后一个完整的DWORD,避免越界。
- 错误处理增强:添加了
GetLastError()的输出,方便调试问题。 - 句柄关闭顺序:先关闭文件映射句柄,再关闭文件句柄,符合Windows API的规范。
你可以测试一下修复后的代码,大文件应该能正常处理了。
内容的提问来源于stack exchange,提问作者tatoru
相关产品推荐
相关产品推荐

