如何将大文件读入缓冲区

时间:2014-08-09 06:38:49

标签: c++ file bigdata

我有一些代码来读取文件:

FILE* file = fopen(fileName.c_str(), "r");
assert(file != NULL);
size_t BUF_SIZE = 10 * 1024 * 1024;
char* buf = new char[BUF_SIZE];
string contents;
while (!feof(file))
{
    int ret = fread(buf, BUF_SIZE, 1, file);
    assert(ret != -1);
    contents.append(buf);
}

我事先知道文件的大小,所以我分配一个缓冲区来存储该行中文件的内容:

char* buf = new char[BUF_SIZE];

如果我需要读取的文件非常大,例如高达几GB,则无法分配几GB的内存来存储文件的内容。否则我不知道需要读取多少文件大小。我该怎么办?

2 个答案:

答案 0 :(得分:2)

首先,您应该知道的是,C运行时中已经存在多层缓冲,并且通常是OS下的缓冲区。如果您无缘无故地添加另一个缓冲层,那么您可能只会放慢速度。

(例如,您可能会发现我的文章"where the printf rubber meets the road"仅仅是为了了解一些glibc在幕后的情况。

其次 - 不要同时将巨大的文件读入连续的内存块。如果您正在执行快速而肮脏的代码,并且您将要执行一次并丢弃,那么有时候它会很好。但它并不是一种在真实程序中使用的技术,你可能会让其他人接受任意大小的输入。

如果您无法了解如何在不访问所有内容的情况下阅读输入,并且您想要处理任意大小的文件,就像它已被加载一样像这样,你可以了解Memory Mapped Files。这可以从操作系统获得一些帮助。

但是,如果每次运行程序时都必须读取一个大文件,其内容具有不可预测的需求,这听起来像是数据库的工作。不要使用freads / fwrites与之交谈,而是将信息加载到其中并在查询和更新方面与之交谈 - 其他人已经解决了其中的大部分复杂问题。

答案 1 :(得分:2)

处理大量文件输入的最简单方法是将文件映射到内存中。这看起来好像已经将整个文件加载到一个巨大的缓冲区中,但它并不要求操作系统实际将所有这些数据同时保存在内存中 - 数据可以以惰性方式读取,并且操作系统是免费的只需重用映射中的内存页面,甚至无需将数据交换回磁盘。

在Linux下,调用是mmap(),在windows下有类似的东西,但我不知道它是如何被调用的。 mmap()函数的使用方式如下:

int file = open(path, O_RDONLY);    //Open the file.
off_t fileLength = lseek(file, 0, SEEK_END);    //Get its size.

//Map its contents into memory.
const char* contents = mmap(NULL, fileLength, PROT_READ, MAP_SHARED, file, 0);

close(file);    //The file can be closed right away, the mapping is not affected.

以任何您想要的方式检查文件。像计算线一样:

off_t lineCount = 0;
for(off_t i = 0; i < fileLength; i++) if(contents[i] == '\n') lineCount++;

最后,您应该使用

清理映射
munmap(input, length);

我遗漏了错误处理以避免混淆代码,但当然,您需要处理任何这些调用产生的任何错误。


当然,在64位操作系统上映射文件是最有利的:映射的大小受虚拟地址空间大小的限制。因此,您将无法在一个32位操作系统上mmap()一个5 GB的文件,这在64位操作系统上没有问题。