我正在尝试使用以下代码读取一行:
while(fscanf(f, "%[^\n\r]s", cLine) != EOF )
{
/* do something with cLine */
}
但不知怎的,我每次只得到第一行。这是一条读线的坏方法吗?我应该怎样修复才能使其按预期工作?
答案 0 :(得分:19)
使用fscanf()
函数几乎总是一个坏主意,因为它可能会在失败时将文件指针留在未知位置。
我更喜欢使用fgets()
来获取每一行,然后使用sscanf()
。然后,您可以根据需要继续检查读入的行。类似的东西:
#define LINESZ 1024
char buff[LINESZ];
FILE *fin = fopen ("infile.txt", "r");
if (fin != NULL) {
while (fgets (buff, LINESZ, fin)) {
/* Process buff here. */
}
fclose (fin);
}
fgets()
似乎就是你要做的事情,读取字符串直到遇到换行符。
答案 1 :(得分:3)
如果你想逐行读取一个文件(这里,行分隔符=='\ n'),只需这样做:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
FILE *fp;
char *buffer;
int ret;
// Open a file ("test.txt")
if ((fp = fopen("test.txt", "r")) == NULL) {
fprintf(stdout, "Error: Can't open file !\n");
return -1;
}
// Alloc buffer size (Set your max line size)
buffer = malloc(sizeof(char) * 4096);
while(!feof(fp))
{
// Clean buffer
memset(buffer, 0, 4096);
// Read a line
ret = fscanf(fp, "%4095[^\n]\n", buffer);
if (ret != EOF) {
// Print line
fprintf(stdout, "%s\n", buffer);
}
}
// Free buffer
free(buffer);
// Close file
fclose(fp);
return 0;
}
享受:)
答案 2 :(得分:1)
如果你试试while( fscanf( f, "%27[^\n\r]", cLine ) == 1 )
,你可能会有更多的运气。原始的三个变化:
27
作为示例,不幸的是scanf()
系列在格式字符串中需要字段宽度,并且不能使用{ {1}} *
可以传递printf()
- s
是“匹配或不匹配集合的所有字符”的格式说明符,并且该集合由%[
终止也就是说,通过使用]
尽可能多地读入适合您缓冲区的行,您将获得相同的结果,同时减少痛苦。
答案 3 :(得分:1)
使用fscanf读取/标记文件总是会导致代码脆弱或痛苦。读取一条线,并对该线进行标记或扫描是安全且有效的。它需要更多代码行 - 这意味着需要更长时间来思考你想要做什么(并且你需要处理一个有限的输入缓冲区大小) - 但在那之后生活只会更少。
不要打fscanf。只是不要使用它。如初。
答案 4 :(得分:0)
在我看来,你正试图在你的fscanf字符串中使用正则表达式运算符。字符串[^\n\r]
对fscanf没有任何意义,这就是为什么你的代码没有按预期工作的原因。
此外,如果项目不匹配,fscanf()不会返回EOF。相反,它返回一个表示匹配数的整数 - 在您的情况下可能为零。 EOF仅在流的末尾或出现错误时返回。所以在你的情况下发生的事情是第一次调用fscanf()一直读到文件的末尾寻找匹配的字符串,然后返回0以告诉你没有找到匹配。然后第二个调用返回EOF,因为已经读取了整个文件。
最后,请注意%s scanf格式运算符仅捕获到下一个空白字符,因此在任何情况下都不需要排除\ n或\ r。
有关详细信息,请参阅fscanf文档:http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/
答案 5 :(得分:0)
你的循环有几个问题。你写道:
while( fscanf( f, "%[^\n\r]s", cLine ) != EOF )
/* do something */;
需要考虑的一些事项:
fscanf()返回存储的项目数。如果它读取超过文件末尾或文件句柄有错误,它可以返回EOF。您需要区分有效的零返回值,在这种情况下,成功读取的缓冲区cLine
中没有新内容。
发生匹配失败时会出现问题,因为很难预测文件句柄现在指向流中的位置。这使得从失败的匹配中恢复比预期的更难。
你写的模式可能不符合你的意图。它匹配任何数量的非CR或LF的字符,然后期望找到文字s
。
您没有保护缓冲区免受溢出。无论分配给该缓冲区的大小如何,都可以从文件中读取任何数量的字符并将其写入缓冲区。这是一个不幸的常见错误,在许多情况下,攻击者可以利用它来运行攻击者选择的任意代码。
除非您特别要求以二进制模式打开f
,否则行结束翻译将在库中发生,您通常不会看到CR字符,通常也不会出现在文本文件中。
你可能想要一个更像下面的循环:
while(fgets(cLine, N_CLINE, f)) {
/* do something */ ;
}
其中N_CLINE是开始cLine
的缓冲区中可用的字节数。
fgets()
函数是从文件中读取一行的首选方法。它的第二个参数是缓冲区的大小,它从文件到缓冲区读取的字节数小于1。它始终使用nul
字符终止缓冲区,以便可以安全地将其传递给其他C字符串函数。
它在文件末尾,换行符或buffer_size-1
字节读取的第一个句点停止。
它将换行符留在缓冲区中,这一事实允许您将比缓冲区长的单行与短于缓冲区的行区分开来。
如果由于文件结束或错误而没有复制字节,则返回NULL,否则返回指向缓冲区的指针。您可能希望使用feof()
和/或ferror()
来区分这些情况。
答案 6 :(得分:0)
我认为这段代码的问题是因为当你用%[^ \ n \ r]来阅读时,事实上,你一直在阅读,直到达到&#39; \ n&#39;或者&#39; \ r&#39;但是你没有阅读过&#39; \ n&#39;或者&#39; \ r&#39;也。 因此,在循环再次使用fscanf读取之前,需要获取此字符。 做那样的事情:
do{
fscanf(f, "%[^\n\r]s", cLine) != EOF
/* Do something here */
}while(fgetc(file) != EOF)