在C语言中将文本文件读入数组

18

如何将文本文件最有效地读入动态一维数组中?在每次读取字符后重新分配内存看起来很愚蠢,每次读取行后重新分配内存也不会好多少。我想将整个文件读入数组中,请问应该如何处理?


我可能误解了您想要做的事情:您是想将整个文件读入一个大缓冲区中,还是想要一个每行都有一个条目的数组? - Christoph
3个回答

26

我不太明白你想要什么。你想逐行处理文件,读一行然后放弃它并处理下一行吗?还是你想将整个文件读入缓冲区?如果你想要后者,我认为这是合适的(在实际代码中检查malloc和fopen的NULL返回值以确定文件是否存在和是否获得足够的内存):

FILE *f = fopen("text.txt", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);

char *bytes = malloc(pos);
fread(bytes, pos, 1, f);
fclose(f);

hexdump(bytes); // do some stuff with it
free(bytes); // free allocated memory

你应该以二进制模式打开文件 - 否则可能会出现问题(例如检查glibc手册,12.17)。 - Christoph
1
你好,(假设我们使用100代替pos) char bytes = malloc(100sizeof(char)); 和上面那行代码 char *bytes = malloc(100); 有什么区别?第二个问题是,如果我的文件中有180205962个字符,那么上述读取文件的方式是否高效? - asel
1
@asel,第一个问题:sizeof(char)被定义为1,因此没有区别。第二个问题:不,你应该逐步阅读它(比如逐行或其他分段方法)。否则,你的内存很快就会耗尽。 - Johannes Schaub - litb
1
使用fseek/ftell获取文件大小是不安全的。请参考CERT参考文献了解原因以及如何安全地执行此操作:https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+file - Bryan
@Bryan:谢谢你提供的链接。顺便说一句,该页面已经移动到https://www.securecoding.cert.org/confluence/display/c/FIO19-C.+Do+not+use+fseek%28%29+and+ftell%28%29+to+compute+the+size+of+a+regular+file。 - David Cary
显示剩余6条评论

12
如果您的系统支持mmap(2),则可以打开文件并将其映射到内存中。这样,您无需分配内存,甚至无需读取文件,系统会完成这些工作。您可以使用litb提供的fseek()技巧来获取文件大小
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

编辑:你必须使用lseek()来获取文件的大小。

int fd = open("filename", O_RDONLY);
int nbytes = lseek(fd, 0, SEEK_END);
void *content = mmap(NULL, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);

@saffsd,你的声望已经足够高了,你知道这里的工作方式,可以修复它。 - philant
忘记了那个,已修复并删除了注释。 - saffsd
1
获取文件大小的可能更习惯的方法是使用 fstat(2) 函数:struct stat S; fstat(fd, &S);,然后 int nbytes = S.st_size 是以字节为单位的文件大小,直接从文件系统中获取,而不需要读取文件(这无疑会得到与上面相同的结果;我主要是为了完整性而提及它)。 - Norman Gray

1

如果您想使用ISO C,请使用此函数

这是litb的答案,带有一些错误处理...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接