You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

修复要点说明

  1. 字节偏移修正:把DWORD索引 * sizeof(DWORD)转成实际的字节偏移,确保映射到正确的文件位置。
  2. 分块单位统一:所有大小计算都用字节,避免混淆DWORD数量和字节数。
  3. 随机数生成优化:uixor里只初始化一次随机数生成器,保证每次生成的mask不同。
  4. 文件末尾处理:如果文件大小不是4的整数倍,处理到最后一个完整的DWORD,避免越界。
  5. 错误处理增强:添加了GetLastError()的输出,方便调试问题。
  6. 句柄关闭顺序:先关闭文件映射句柄,再关闭文件句柄,符合Windows API的规范。

你可以测试一下修复后的代码,大文件应该能正常处理了。

内容的提问来源于stack exchange,提问作者tatoru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:31:22