使用MapViewOfFile映射大型文件

Ale*_*ich 4 c++ winapi file-mapping

我有一个非常大的文件,我需要以小块读取它,然后处理每个部分.我正在使用MapViewOfFile函数来映射内存中的一块,但在读完第一部分后我无法读取第二部分.当我试图映射它时它会抛出.

    char *tmp_buffer = new char[bufferSize];
    LPCWSTR input = L"input";   
    OFSTRUCT tOfStr;
    tOfStr.cBytes = sizeof tOfStr;

    HANDLE inputFile = (HANDLE)OpenFile(inputFileName, &tOfStr, OF_READ); 
    HANDLE fileMap = CreateFileMapping(inputFile, NULL, PAGE_READONLY, 0, 0, input);

    while (offset < fileSize)
    {
        long k = 0;
        bool cutted = false;
        offset -= tempBufferSize;

        if (fileSize - offset <= bufferSize)
        {
            bufferSize = fileSize - offset;
        }

        char *buffer = new char[bufferSize + tempBufferSize];

        for(int i = 0; i < tempBufferSize; i++)
        {
            buffer[i] = tempBuffer[i];
        }

        char *tmp_buffer = new char[bufferSize];
        LPCWSTR input = L"input";
        HANDLE inputFile;
        OFSTRUCT tOfStr;
        tOfStr.cBytes = sizeof tOfStr;

        long long offsetHigh = ((offset >> 32) & 0xFFFFFFFF);
        long long offsetLow = (offset & 0xFFFFFFFF);

        tmp_buffer = (char *)MapViewOfFile(fileMap, FILE_MAP_READ, (int)offsetHigh, (int)offsetLow, bufferSize);

        memcpy(&buffer[tempBufferSize], &tmp_buffer[0], bufferSize);

        UnmapViewOfFile(tmp_buffer);

        offset += bufferSize;
        offsetHigh = ((offset >> 32) & 0xFFFFFFFF);
        offsetLow = (offset & 0xFFFFFFFF);

        if (offset < fileSize)
        {
            char *next;
            next = (char *)MapViewOfFile(fileMap, FILE_MAP_READ, (int)offsetHigh, (int)offsetLow, 1);

            if (next[0] >= '0' && next[0] <= '9')
            {
                cutted = true;
            }

            UnmapViewOfFile(next);
        }

        ostringstream path_stream;
        path_stream << tempPath << splitNum;

        ProcessChunk(buffer, path_stream.str(), cutted, bufferSize);

        delete buffer;

        cout << (splitNum + 1) << " file(s) sorted" << endl;
        splitNum++;
    }
Run Code Online (Sandbox Code Playgroud)

Adr*_*thy 6

一种可能性是您没有使用偏移量是分配粒度的倍数.来自MSDN:

高偏移量和低偏移量的组合必须指定文件映射中的偏移量.它们还必须匹配系统的内存分配粒度.也就是说,偏移量必须是分配粒度的倍数.要获取系统的内存分配粒度,请使用GetSystemInfo函数,该函数填充SYSTEM_INFO结构的成员.

如果您尝试映射除分配粒度的倍数之外的其他内容,映射将失败GetLastError并将返回ERROR_MAPPED_ALIGNMENT.

除此之外,代码示例中存在许多问题,这使得很难看到您正在尝试做什么以及哪里出错.至少,您需要解决内存泄漏问题.你似乎在分配,然后泄漏完全不必要的缓冲区.给他们更好的名字可以清楚地说明他们实际使用的是什么.

然后我建议在对MapViewOfFile的调用上放置一个断点,然后检查你传入的所有参数值,以确保它们看起来正确.首先,在第二次调用时,您希望offsetHigh为0,offsetLow为bufferSize.

一些可疑的事情:

HANDLE inputFile = (HANDLE)OpenFile(inputFileName, &tOfStr, OF_READ); 
Run Code Online (Sandbox Code Playgroud)

每个演员应该让你怀疑.有时他们是必要的,但要确保你理解为什么.此时你应该问自己为什么你正在使用的每个其他文件API都需要一个,HANDLE而这个函数返回一个HFILE.如果检查OpenFile文档,您将看到"此功能的功能有限,不建议使用.对于新的应用程序开发,请使用CreateFile函数." 我知道这听起来很混乱,因为你想打开一个现有文件,但CreateFile可以做到这一点,并返回正确的类型.

long long offsetHigh = ((offset >> 32) & 0xFFFFFFFF);
Run Code Online (Sandbox Code Playgroud)

什么类型offset?你可能想确保它是一个unsigned long long或同等的.当位移时,尤其是向右移位时,您几乎总是希望使用无符号类型来避免符号扩展.你还必须确保它的位数比你移位的位数多 - 在32位(或更多)位上移位32位值实际上在C和C++中是未定义的,这允许编译器做某些类型的优化.

long long offsetLow = (offset & 0xFFFFFFFF);
Run Code Online (Sandbox Code Playgroud)

在这两个陈述中,您必须小心该0xFFFFFFFF值.由于您没有强制转换或给它后缀,因此很难预测编译器是将其视为int还是unsigned int.在这种情况下,它将是一个unsigned int,但对许多人来说这并不明显.事实上,当我第一次写这个答案时,我弄错了.[ 本段修正于2017年5月16日 ]通过按位运算,您几乎总是希望确保使用无符号值.

tmp_buffer = (char *)MapViewOfFile(fileMap, FILE_MAP_READ, (int)offsetHigh, (int)offsetLow, bufferSize);
Run Code Online (Sandbox Code Playgroud)

你铸造offsetHighoffsetLowintS,被签名的值.API实际上需要DWORDs,它们是无符号值.我会声明offsetHighoffsetLow作为DWORDs并在初始化中执行转换,而不是在调用中进行转换,如下所示:

DWORD offsetHigh = static_cast<DWORD>((offset >> 32) & 0xFFFFFFFFul);
DWORD offsetLow  = static_cast<DWORD>( offset        & 0xFFFFFFFFul);
tmp_buffer = reinterpret_cast<const char *>(MapViewOfFile(fileMap, FILE_MAP_READ, offsetHigh, offsetLow, bufferSize));
Run Code Online (Sandbox Code Playgroud)

这些修复可能会也可能不会解决您的问题.很难说出不完整的代码示例会发生什么.

这是一个可以比较的工作样本:

// Calls ProcessChunk with each chunk of the file.
void ReadInChunks(const WCHAR *pszFileName) {
  // Offsets must be a multiple of the system's allocation granularity.  We
  // guarantee this by making our view size equal to the allocation granularity.
  SYSTEM_INFO sysinfo = {0};
  ::GetSystemInfo(&sysinfo);
  DWORD cbView = sysinfo.dwAllocationGranularity;

  HANDLE hfile = ::CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ,
                               NULL, OPEN_EXISTING, 0, NULL);
  if (hfile != INVALID_HANDLE_VALUE) {
    LARGE_INTEGER file_size = {0};
    ::GetFileSizeEx(hfile, &file_size);
    const unsigned long long cbFile =
        static_cast<unsigned long long>(file_size.QuadPart);

    HANDLE hmap = ::CreateFileMappingW(hfile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (hmap != NULL) {
      for (unsigned long long offset = 0; offset < cbFile; offset += cbView) {
        DWORD high = static_cast<DWORD>((offset >> 32) & 0xFFFFFFFFul);
        DWORD low  = static_cast<DWORD>( offset        & 0xFFFFFFFFul);
        // The last view may be shorter.
        if (offset + cbView > cbFile) {
          cbView = static_cast<int>(cbFile - offset);
        }
        const char *pView = static_cast<const char *>(
            ::MapViewOfFile(hmap, FILE_MAP_READ, high, low, cbView));
        if (pView != NULL) {
          ProcessChunk(pView, cbView);
        }
      }
      ::CloseHandle(hmap);
    }
    ::CloseHandle(hfile);
  }
}
Run Code Online (Sandbox Code Playgroud)