我想逐行阅读文件。我fgets()
工作正常,但如果一行超过我传递给fgets()
的缓冲区大小,我不知道该怎么办?此外,由于fgets()
似乎不支持Unicode,并且我想允许UTF-8文件,它可能会错过行结尾并读取整个文件,不是吗?
然后我想我会使用getline()
。但是,我在Mac OS X上,虽然在getline()
中指定了/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/usr/include/stdio.h
,但它不在/usr/include/stdio
中,因此gcc
在shell中找不到它。显然,它并不是特别便携,而且我希望我正在开发的库通常很有用。
那么在C中逐行读取文件的最佳方法是什么?
答案 0 :(得分:1)
首先,您不太可能担心像U+2028这样的非标准行终结符。普通文本文件不应包含它们,并且绝大多数读取普通文本文件的现有软件都不支持它们。你提到了glibc中提供的getline()
,但是没有提到MacOS的libc,如果getline()
支持这种奇特的行终止符,我会感到惊讶。几乎可以肯定的是,你只需支持LF(U + 000A),也可以支持CR + LF(U + 000D U + 000A)。要做到这一点,您不需要关心UTF-8。这是UTF-8的ASCII兼容性的美感,并且是设计的。
对于比传递给fgets()
的缓冲区更长的支持行,你可以在fgets周围添加一些额外的逻辑。在伪代码中:
while true {
fgets(buffer, size, stream);
dynamically_allocated_string = strdup(buffer);
while the last char (before the terminating NUL) in the buffer is not '\n' {
concatenate the contents of buffer to the dynamically allocated string
/* the current line is not finished. read more of it */
fgets(buffer, size, stream);
}
process the whole line, as found in the dynamically allocated string
}
但是再一次,我认为你会发现那里有很多软件根本没有那么麻烦,从解析系统配置文件(如/etc/passwd
)到(某些)脚本语言的软件。根据您的使用情况,使用“足够大”的缓冲区(例如4096字节)并声明您不支持超过该行的行可能非常好。您甚至可以将其称为安全功能(线路长度限制可防止来自精心设计的输入文件的资源耗尽攻击)。
答案 1 :(得分:0)
基于this answer,这就是我的想法:
#define LINE_BUF_SIZE 1024
char * getline_from(FILE *fp) {
char * line = malloc(LINE_BUF_SIZE), * linep = line;
size_t lenmax = LINE_BUF_SIZE, len = lenmax;
int c;
if(line == NULL)
return NULL;
for(;;) {
c = fgetc(fp);
if(c == EOF)
break;
if(--len == 0) {
len = lenmax;
char * linen = realloc(linep, lenmax *= 2);
if(linen == NULL) {
// Fail.
free(linep);
return NULL;
}
line = linen + (line - linep);
linep = linen;
}
if((*line++ = c) == '\n')
break;
}
*line = '\0';
return linep;
}
阅读stdin
:
char *line;
while ( line = getline_from(stdin) ) {
// do stuff
free(line);
}
要阅读其他文件,我首先使用fopen()
打开它:
FILE *fp;
fp = fopen ( filename, "rb" );
if (!fp) {
fprintf(stderr, "Cannot open %s: ", argv[1]);
perror(NULL);
exit(1);
}
char *line;
while ( line = getline_from(fp) ) {
// do stuff
free(line);
}
这对我很有用。我很乐意看到使用fgets()
作为suggested by @paul-tomblin的替代方案,但我没有精力在今晚解决这个问题。