使用c在Linux上读取和写入数据块

我有一个ASCII文件,每行包含一个可变长度的logging。 例如

Record-1:15 characters Record-2:200 characters Record-3:500 characters ... ... Record-n: X characters 

由于文件大小约为10GB,我想以大块的方式读取logging。 一旦阅读,我需要转换它们,把它们写入另一个二进制格式的文件。

所以,为了阅读,我的第一反应是创build一个char数组,如

 FILE *stream; char buffer[104857600]; //100 MB char array fread(buffer, sizeof(buffer), 104857600, stream); 
  1. 是否正确假设,Linux将发出一个系统调用,并获取整个100MB?
  2. 由于logging以换行符分隔,因此我在缓冲区中逐字符地search新的行字符并重build每条logging。

我的问题是,这是我应该如何阅读大块或有更好的select来读取数据的大块和重build每个logging? 在一次调用中,是否有另一种方法从ASCII文件中读取x个可变大小的行?

接下来在写,我也这样做。 我有一个写入字符缓冲区,我通过fwrite写一整套logging在一个电话。

 fwrite(buffer, sizeof(buffer), 104857600, stream); 

更新:如果我setbuf(stream,缓冲区),其中缓冲区是我的100MB字符缓冲区,将fgets从缓冲区返回或导致磁盘IO?

  1. 是的, fread会立刻拿起整个东西。 (假设它是一个普通的文件),但它不会读取105MB,除非文件本身是105MB,如果你不检查返回值,你无法知道有多少数据被实际读取,或者如果有是一个错误。

  2. 使用fgets (请参阅man fgets )而不是fread 。 这将为您搜索换行符。

     char linebuf[1000]; FILE *file = ...; while (fgets(linebuf, sizeof(linebuf), file) { // decode one line } 
  3. 你的代码有问题。

     char buffer[104857600]; // too big 

    如果你尝试在堆栈上分配一个大的缓冲区(105 MB肯定是大的),那么它将失败,你的程序将崩溃。 如果你需要一个很大的缓冲区,你必须在malloc或类似的堆上分配它。 尽管在大多数股票Linux系统上可能会有几MB,但是我肯定会把堆栈的使用量保持在几十KB之内。

作为替代方案,您可以将整个文件mmap到内存中。 在大多数情况下,这不会改善或降低性能,但更容易处理。

 int r, fdes; struct stat st; void *ptr; size_t sz; fdes = open(filename, O_RDONLY); if (fdes < 0) abort(); r = fstat(fdes, &st); if (r) abort(); if (st.st_size > (size_t) -1) abort(); // too big to map sz = st.st_size; ptr = mmap(NULL, sz, PROT_READ, MAP_SHARED, fdes, 0); if (ptr == MAP_FAILED) abort(); close(fdes); // file no longer needed // now, ptr has the data, sz has the data length // you can use ordinary string functions 

使用mmap的好处是你的程序不会耗尽内存。 在64位系统上,可以将整个文件同时放入地址空间(甚至是10 GB的文件),系统会在您的程序访问内存时自动读取新的块。 旧的块将自动丢弃,如果您的程序再次需要,则重新读取。

这是一个非常好的方式来处理大文件。

如果可以的话,你可能会发现mmap文件是最简单的。 mmap将(文件的一部分)映射到内存中,这样整个文件就可以作为一个字节数组来访问。 在你的情况下,你可能无法一次映射整个文件,看起来像这样:

 #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <sys/mman.h> /* ... */ struct stat stat_buf; long pagesz = sysconf(_SC_PAGESIZE); int fd = fileno(stream); off_t line_start = 0; char *file_chunk = NULL; char *input_line; off_t cur_off = 0; off_t map_offset = 0; /* map 16M plus pagesize to ensure any record <= 16M will always fit in the mapped area */ size_t map_size = 16*1024*1024+pagesz; if (map_offset + map_size > stat_buf.st_size) { map_size = stat_buf.st_size - map_offset; } fstat(fd, &stat_buf); /* map the first chunk of the file */ file_chunk = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, map_offset); // until we reach the end of the file while (cur_off < stat_buf.st_size) { /* check if we're about to read outside the current chunk */ if (!(cur_off-map_offset < map_size)) { // destroy the previous mapping munmap(file_chunk, map_size); // round down to the page before line_start map_offset = (line_start/pagesz)*pagesz; // limit mapped region to size of file if (map_offset + map_size > stat_buf.st_size) { map_size = stat_buf.st_size - map_offset; } // map the next chunk file_chunk = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, map_offset); // adjust the line start for the new mapping input_line = &file_chunk[line_start-map_offset]; } if (file_chunk[cur_off-map_offset] == '\n') { // found a new line, process the current line process_line(input_line, cur_off-line_start); // set up for the next one line_start = cur_off+1; input_line = &file_chunk[line_start-map_offset]; } cur_off++; } 

大部分的复杂性是为了避免做太大的映射。 您可能可以使用映射整个文件

 char *file_data = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0); 

我的意见是使用fgets(buff)来自动检测新行。

然后用strlen(buff)来计算缓冲区大小,

 if( (total+strlen(buff)) > 104857600 ) 

然后写入新的块。

但块的大小几乎不会是104857600字节。

CMIIW