首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用MapViewOfFile映射大文件

使用MapViewOfFile映射大文件
EN

Stack Overflow用户
提问于 2012-03-27 20:29:45
回答 3查看 21.5K关注 1票数 5

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

代码语言:javascript
复制
    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++;
    }
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-03-28 01:04:49

一种可能是您使用的偏移量不是分配粒度的倍数。来自MSDN:

高偏移量和低偏移量的组合必须指定文件映射内的偏移量。它们还必须与系统的内存分配粒度相匹配。即,偏移量必须是分配粒度的倍数。要获得系统的内存分配粒度,可以使用GetSystemInfo函数,该函数填充SYSTEM_INFO结构的成员。

如果您尝试以分配粒度的倍数以外的值进行映射,映射将失败,并且GetLastError将返回ERROR_MAPPED_ALIGNMENT

除此之外,代码示例中还有许多问题,这些问题使您很难看到您正在尝试做的事情以及它在哪里出错。至少,您需要解决内存泄漏问题。您似乎正在分配并泄漏完全不必要的缓冲区。赋予它们更好的名称可以清楚地表明它们的实际用途。

然后,我建议在对MapViewOfFile的调用中设置断点,然后检查您传递的所有参数值,以确保它们看起来正确。首先,在第二次调用时,您会期望offsetHigh为0,offsetLow为bufferSize。

一开始就有一些可疑的事情:

代码语言:javascript
复制
HANDLE inputFile = (HANDLE)OpenFile(inputFileName, &tOfStr, OF_READ); 

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

代码语言:javascript
复制
long long offsetHigh = ((offset >> 32) & 0xFFFFFFFF);

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

代码语言:javascript
复制
long long offsetLow = (offset & 0xFFFFFFFF);

在这两个语句中,您必须注意0xFFFFFFFF的值。由于您没有强制转换它或给它一个后缀,所以很难预测编译器会将它视为int还是unsigned int。在这种情况下,它将是一个无符号的int,但这对许多人来说并不明显。事实上,当我第一次写这个答案的时候,我把这个搞错了。本段使用位操作更正了2017年5月16日的,你几乎总是想要确保你使用的是无符号值。

代码语言:javascript
复制
tmp_buffer = (char *)MapViewOfFile(fileMap, FILE_MAP_READ, (int)offsetHigh, (int)offsetLow, bufferSize);

offsetHighoffsetLow转换为ints,这是有符号的值。应用程序接口实际上需要DWORDs,它们是无符号值。我不会在调用中强制转换,而是将offsetHighoffsetLow声明为DWORDs,并在初始化中进行强制转换,如下所示:

代码语言:javascript
复制
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));

这些修复可能会也可能不会解决您的问题。很难从不完整的代码样本中判断出发生了什么。

以下是您可以与之比较的工作示例:

代码语言:javascript
复制
// 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);
  }
}
票数 7
EN

Stack Overflow用户

发布于 2012-03-27 21:06:55

您的代码中存在内存泄漏:

代码语言:javascript
复制
char *tmp_buffer = new char[bufferSize];
[ ... ]
while (offset < fileSize)
{
[ ... ]
    char *tmp_buffer = new char[bufferSize];
[ ... ]
    tmp_buffer = (char *)MapViewOfFile(fileMap, FILE_MAP_READ, (int)offsetHigh, (int)offsetLow, bufferSize);
[ ... ]
}

你永远不会delete你在每次迭代中通过new char[]分配的东西。如果你的文件足够大/你做了足够多的循环迭代,内存分配最终会失败--然后你会看到分配器做了一个throw()

MapViewOfFile()这样的Win32应用程序接口调用不是C++,也不会抛出,它们会返回错误代码(后者在失败时为NULL )。因此,如果您看到异常,则说明您的C++代码中存在问题。很可能是上面所说的。

票数 1
EN

Stack Overflow用户

发布于 2012-09-24 09:38:11

我在内存映射文件方面也遇到了一些问题。基本上,我只是想在同一个Pc上的两个应用程序之间共享内存(1Mo)。-两个应用程序都是用Delphi编写的-使用Windows8专业版

首先,一个应用程序(启动的第一个应用程序)可以读写memoryMappedFile,但第二个应用程序只能读取它(error 5 : AccessDenied)

最后,在经过大量测试后,当两个应用程序都使用CreateFileMapping时,它突然开始工作了。我甚至尝试创建我的on安全描述符,但没有任何帮助。

在我的应用程序之前,如果第一个调用失败,则首先调用OpenFileMapping,然后调用CreateFileMapping

另一件误导我的事情是句柄,尽管明显地引用了两个应用程序中不同的相同MemoryMappedFile

最后一件事,经过这次修正后,我的应用程序似乎工作正常,但过了一段时间,我就有了error_NotEnough_Memory。调用MapViewOfFile时。这只是我的一个初学者的错误,我并不总是调用UnmapViewOfFile。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9889557

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档