首页 > 解决方案 > 在 Windows 上将文件映射到 C 中的虚拟内存

问题描述

在 POSIX 系统上,我可以使用 mmap 函数比 getline、getc 等更快地读取文件的内容。这在我正在开发的程序中很重要,因为它需要将非常大的文件读入内存;使用 getline 迭代收集行的成本太高。可移植性也是我的软件的要求,所以如果我使用 mmap,我需要找到一种使用 WinApi 来存储映射文件的方法,因为我宁愿不通过 cygwin/msys 编译。通过粗略的搜索,我发现了这篇MSDN 文章,它非常简要地描述了一种将文件映射到内存中的方法,但是,从浏览文档中我无法确定如何实际实现它,而且我一直在寻找示例代码片段,就像 POSIX mmap 的代码片段一样。

如何使用 WinApi 的内存映射选项将文件读入 char*?

标签: cwinapiposixmmap

解决方案


如何使用 WinApi 的内存映射选项将文件读入 char*?

在 Windows 下,当你在内存中映射一个文件时,你会得到一个指向文件第一个字节被映射的内存位置的指针。您可以将该指针转换为您喜欢的任何数据类型,包括 char*。

换句话说,由 Windows 决定映射数据在内存中的位置。您不能提供 char* 并期望 Windows 会在那里加载数据。

这意味着如果您已经有一个 char* 并且想要该 char* 指向的位置中的文件中的数据,那么您必须复制它。在性能方面不是一个好主意。

这是一个通过将文件映射到内存然后显示所有 ASCII 字符来转储文本文件的简单程序。使用 MSVC2019 测试。

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[])
{
    TCHAR *lpFileName = TEXT("hello.txt");
    HANDLE hFile;
    HANDLE hMap;
    LPVOID lpBasePtr;
    LARGE_INTEGER liFileSize;

    hFile = CreateFile(lpFileName, 
        GENERIC_READ,                          // dwDesiredAccess
        0,                                     // dwShareMode
        NULL,                                  // lpSecurityAttributes
        OPEN_EXISTING,                         // dwCreationDisposition
        FILE_ATTRIBUTE_NORMAL,                 // dwFlagsAndAttributes
        0);                                    // hTemplateFile
    if (hFile == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "CreateFile failed with error %d\n", GetLastError());
        return 1;
    }

    if (!GetFileSizeEx(hFile, &liFileSize)) {
        fprintf(stderr, "GetFileSize failed with error %d\n", GetLastError());
        CloseHandle(hFile);
        return 1;
    }

    if (liFileSize.QuadPart == 0) {
        fprintf(stderr, "File is empty\n");
        CloseHandle(hFile);
        return 1;
    }

    hMap = CreateFileMapping(
        hFile,
        NULL,                          // Mapping attributes
        PAGE_READONLY,                 // Protection flags
        0,                             // MaximumSizeHigh
        0,                             // MaximumSizeLow
        NULL);                         // Name
    if (hMap == 0) {
        fprintf(stderr, "CreateFileMapping failed with error %d\n", GetLastError());
        CloseHandle(hFile);
        return 1;
    }

    lpBasePtr = MapViewOfFile(
        hMap,
        FILE_MAP_READ,         // dwDesiredAccess
        0,                     // dwFileOffsetHigh
        0,                     // dwFileOffsetLow
        0);                    // dwNumberOfBytesToMap
    if (lpBasePtr == NULL) {
        fprintf(stderr, "MapViewOfFile failed with error %d\n", GetLastError());
        CloseHandle(hMap);
        CloseHandle(hFile);
        return 1;
    }

    // Display file content as ASCII charaters
    char *ptr = (char *)lpBasePtr;
    LONGLONG i = liFileSize.QuadPart;
    while (i-- > 0) {
        fputc(*ptr++, stdout);
    }

    UnmapViewOfFile(lpBasePtr);
    CloseHandle(hMap);
    CloseHandle(hFile);

    printf("\nDone\n");
}

推荐阅读