【问题标题】:How to read huge file in c++如何在 C++ 中读取大文件
【发布时间】:2016-04-17 13:16:00
【问题描述】:

如果我有一个巨大的文件(例如 1TB,或任何不适合 RAM 的大小。文件存储在磁盘上)。它由空格分隔。我的内存只有 8GB。我可以在 ifstream 中读取该文件吗?如果没有,如何读取一个文件块(例如4GB)?

【问题讨论】:

  • 如何分隔?它是面向行的文本吗?你能一次读一行吗?
  • @nicomp 我怀疑一个文本文件的大小可能为 1 TB。
  • 没有办法。您不能将 1TB 放入任何小于该值的 RAM。如果您想从该文件中提取数据,这是可能的。
  • 你试过阅读吗?如果不是,为什么不呢?如果你这样做了,什么不起作用?
  • @OlegAndriyanov 要我给你发一份吗?

标签: c++ file ifstream


【解决方案1】:

您可以使用fread

char buffer[size];
fread(buffer, size, sizeof(char), fp);

或者,如果您想使用 C++ fstreams,您可以使用 read,正如 buratino 所说。

另外请记住,无论文件大小如何,您都可以打开文件,我们的想法是打开它并在适合您 RAM 的卡盘中读取它。

【讨论】:

  • 他询问了ifstream。我相信更相关的函数调用是read
  • 我阅读了 fread 文档。那么如果我使用FILE * pFile; pFile = fopen ( "myfile.txt" , "rb" ); 并且myfile.txt 无法放入RAM,我还能这样打开吗?
  • fopen 不会在 ram 中加载文件,所以是的,你应该可以毫无问题地做到这一点。
【解决方案2】:

一种更先进的方法是,您可以使用平台特定的 api 将其映射到内存,而不是读取整个文件或其块到内存:

windows下:CreateFileMapping()、MapViewOfFile()

linux下:open(2) / creat(2), shm_open, mmap

您需要编译 64 位应用程序才能使其工作。

更多详情请看这里:CreateFileMapping, MapViewOfFile, how to avoid holding up the system memory

【讨论】:

    【解决方案3】:

    您可以做几件事。

    首先,打开大于您拥有的 RAM 容量的文件没有问题。您无法将整个文件 live 复制到您的内存中。最好的办法是让您找到一种方法,一次只读取几块并处理它们。您可以为此目的使用ifstream(例如,使用ifstream.read)。分配,比如说,一兆字节的内存,将该文件的第一兆字节读入其中,冲洗并重复:

    ifstream bigFile("mybigfile.dat");
    constexpr size_t bufferSize = 1024 * 1024;
    unique_ptr<char[]> buffer(new char[bufferSize]);
    while (bigFile)
    {
        bigFile.read(buffer.get(), bufferSize);
        // process data in buffer
    }
    

    另一种解决方案是将文件映射到内存。大多数操作系统都允许您将文件映射到内存,即使它大于您拥有的物理内存量。这是因为操作系统知道与文件关联的每个内存页面都可以按需映射和取消映射:当您的程序需要特定页面时,操作系统会将其从文件中读取到您的进程的内存中并换出一个页面好久没用了。

    但是,这只有在文件小于您的进程理论上可以使用的最大内存量时才有效。对于 64 位进程中的 1TB 文件,这不是问题,但在 32 位进程中无法正常工作。

    还有be aware of the spirits that you're summoning。内存映射文件与读取文件不同。如果文件突然从另一个程序中截断,您的程序可能会崩溃。如果您修改数据,如果您无法保存回磁盘,则可能会耗尽内存。此外,您的操作系统用于调入和调出内存的算法可能不会以显着优势的方式运行。由于存在这些不确定性,我会考虑仅在使用第一种解决方案分块读取文件不起作用时才考虑映射文件。

    在 Linux/OS X 上,您可以使用 mmap。在 Windows 上,您将打开一个文件,然后使用CreateFileMapping,然后使用MapViewOfFile

    【讨论】:

    • 旁注:常见的错误;未测试流操作:while (bigFile) { bigFile.read(...); ... }
    • 如果 bigFile.read() 读取的数量少于请求的数量,则 nread 数量在 bigFile.gcount() 中。我们可以使用这个 nread 值来索引缓冲区并继续读取循环吗?
    【解决方案4】:

    我相信您不必将所有文件都保存在内存中。通常,人们想要逐块读取和处理文件。如果你想使用ifstream,你可以这样做:

    ifstream is("/path/to/file");
    char buf[4096];
    do {
        is.read(buf, sizeof(buf));
        process_chunk(buf, is.gcount());
    } while(is);
    

    【讨论】:

    • 有没有可能is.read() 有时只能读取不到4096 字节?
    • @typelogic 是的,如果已到达文件末尾,或者发生某种读取错误,read 可以读取的字节数少于请求的字节数。见en.cppreference.com/w/cpp/io/basic_istream/read
    • 抱歉,我的主要问题是我们可以在 nread 小于请求的中断中间继续读取循环。 ifstream 对象还正常吗?我问是因为中断的read 在循环内的 C 中很常见,read 继续。
    • @typelogic 短读并不直接表示错误。如果“理智”是指“我可以继续从流中读取”,那么我相信您应该测试fail() 的返回值。此外,如果您关心精确的错误处理和信号中断等低级内容,您可能希望坚持使用 C 和原始 POSIX read() — API 和文档更加简洁和信息丰富。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多