我有一些代码来读取文件:
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的内存来存储文件的内容。否则我不知道需要读取多少文件大小。我该怎么办?
答案 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位操作系统上没有问题。