我有一个ASCII文件,其中每一行都包含一个可变长度的记录。例如
Record-1:15 characters
Record-2:200 characters
Record-3:500 characters
...
...
Record-n: X characters
由于文件大小约为10 in,所以我想用块读取记录。一旦读取,我需要转换它们,将它们写入另一个二进制格式的文件中。
因此,对于阅读,我的第一个反应是创建一个char数组,如
FILE *stream;
char buffer[104857600]; //100 MB char array
fread(buffer, sizeof(buffer), 104857600, stream);
我的问题是,这是我应该如何读块,还是有更好的选择来读取数据块和重建每一个记录?是否有其他方法可以在一个调用中从ASCII文件中读取x个可变大小的行?
接下来在写的时候,我也是这样做的。我有一个写字符缓冲区,我将它传递给fwrite,以便在一个调用中写入一整组记录。
fwrite(buffer, sizeof(buffer), 104857600, stream);
更新:如果我使用setbuf(流、缓冲区),其中缓冲区是我的100 my的char缓冲区,那么fget会从缓冲区返回还是导致磁盘IO?
发布于 2012-05-09 19:53:09
fread
会立刻把整个东西取回来。(假设它是一个常规文件。)但是,除非文件本身是105 MB,否则它不会读取105 MB,而且如果不检查返回值,就无法知道实际读取了多少数据,或者是否有错误。fgets
(参见man fgets
)而不是fread
。这将为您搜索换行处。char linebuf1000;FILE *file =.;而(fget(linebuf,sizeof(linebuf),file) { // decode单行}
char buffer104857600;//太大
如果您试图在堆栈上分配一个大缓冲区(105 MB当然很大),那么它将失败,您的程序将崩溃。如果需要这么大的缓冲区,就必须使用malloc
或类似的方法在堆中分配它。我当然会保留单个函数最多几十KB的堆栈使用情况,尽管在大多数股票Linux系统上您可能可以使用几MB。
作为一种选择,您只需将整个文件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文件),当程序访问内存时,系统将自动读取新的块。旧块将自动丢弃,如果您的程序再次需要它们,则重新读取它们。
这是一个很好的方式,犁大文件。
发布于 2012-05-09 20:38:16
如果可以的话,您可能会发现对该文件进行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);
发布于 2012-05-09 19:53:41
我的观点是使用fgets(buff)
自动检测新线路。
然后使用strlen(buff)
来计算缓冲区大小,
if( (total+strlen(buff)) > 104857600 )
然后用新的块写..。
但是这个块的大小几乎不可能是104857600字节。
CMIIW
https://stackoverflow.com/questions/10527187
复制相似问题