将大块读入内存并逐行处理

时间:2016-10-20 01:42:04

标签: performance file c++11

我希望有效地阅读和处理大型文本文件,根据SO上的许多帖子,建议将大块文件读入内存并在内存中进行处理。

我尝试这样做:

FILE* fp = fopen(path, "r");
char chunk[SIZE];
while (fread(chunk, SIZE, 1, fp)) {
  process_chunk_line_by_line(chunk);
}

问题是,如果chunk中的最后一行不完整,我不应该处理最后一行,应该按不完整的最后一行的长度来寻找fp怎么办?有没有更有效的方法来做到这一点?

1 个答案:

答案 0 :(得分:2)

这是我知道通过使用已有的stdio库函数来执行您想要的操作的最简单方法。 (stdio因为那是你已经使用过的以及你似乎熟悉的东西。还有其他方法可以用C ++流来完成。)

使用stdio缓冲区输入打开的

fopen()个文件,您的操作系统可能使用页面缓存。在应用程序中添加另一层缓冲意味着磁盘上的数据和处理数据的应用程序之间会有三层缓冲层:1)页面缓存2)stdio缓冲区, 3)你的chunk。正如@ShadowRanger所评论 - 只需使用stdio缓冲区,然后您可以使用standard getline() function来读取行。

// change size to suit your requirements
#define BUFSIZE ( 16UL * 1024UL * 1024UL )
FILE *fp = fopen( path, "rb" );

// assuming a POSIX OS - could also use malloc()/free()
char *buffer = ( char * ) mmap( NULL, BUFSIZE, PROT_READ | PROT_WRITE,
    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
setvbuf( fp, buffer, _IOFBF, BUFSIZE );

char *line = NULL;
size_t len = 0;

for ( ;; )
{
    ssize_t currentLen = getline( &line, &len, fp );
    if ( currentLen < 0 )
    {
        break;
    }

    // process line
}

free( line );
fclose( fp );
munmap( buffer, BUFSIZE );

您需要添加错误检查以及正确的头文件。

这应该完全符合您的要求,并且不必编写代码,必须找出行结束的位置,也不必处理跨越多个行的行fread()来电。{/ p>

如果绕过页面缓存,它可能会更快。上面的代码已经使用了16 MB的缓存。额外的缓存只是在从磁盘到应用程序的数据路径中添加了另一个副本。由于您不需要寻求,并且您不会重新读取数据,因此使用此模式的页面缓存对您没有好处。在Linux上,您的文件系统支持直接IO,您可以这样做:

int fd = open( path, O_RDONLY | O_DIRECT );
FILE *fp = fdopen( fd, "rb" );

请注意,直接IO有很大的限制 - 您的IO缓冲区可能必须页面对齐。关于mmap()的一个好处是它返回页面对齐的内存...

如果文件系统支持直接IO,那将绕过页面缓存,并且您的读取操作可能会大大加快,并且可能会给您的计算机带来更少的内存压力,尤其是在文件非常大的情况下。