使用fscanf()读取一行时遇到问题

我正在尝试使用以下代码读取一行:

while(fscanf(f, "%[^\n\r]s", cLine) != EOF ) { /* do something with cLine */ } 

但不知怎的,我每次只得到第一行。 这是一条读线的坏方法吗? 我该怎么办才能让它按预期工作?

使用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()似乎是你想要做的,读取字符串直到遇到换行符。

如果你想逐行读取一个文件(这里,行分隔符==’\ n’),只需要:

 #include  #include  #include  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; } 

请享用 :)

如果你尝试的话while( fscanf( f, "%27[^\n\r]", cLine ) == 1 )你可能会有更多的运气。 原始的三个变化:

  • 读取的长度限制 – 我在这里使用了27作为示例,不幸的是scanf()系列在格式字符串中需要字段宽度,并且不能使用printf()可以传递的*机制中的价值
  • 摆脱格式字符串中的s%[是“匹配或不匹配集合的所有字符”的格式说明符,并且该集合由a自己终止
  • 将返回值与您预期发生的转换次数进行比较(为了便于管理,请确保该数字为1)

也就是说,通过使用fgets()读取适合缓冲区的行数,您可以获得相同的结果,减少痛苦。

使用fscanf读取/标记文件总是会导致代码脆弱或痛苦。 读取一条线,并对该线进行标记或扫描是安全且有效的。 它需要更多的代码行 – 这意味着需要更长时间来思考你想要做什么(并且你需要处理有限的输入缓冲区大小) – 但在那之后生活只会更少。

不要打fscanf。 只是不要使用它。 永远。

在我看来,你正试图在你的fscanf字符串中使用正则表达式运算符。 字符串[^\n\r]对fscanf没有任何意义,这就是为什么你的代码不能按预期工作的原因。

此外,如果项目不匹配,fscanf()不会返回EOF。 相反,它返回一个表示匹配数的整数 – 在您的情况下可能为零。 EOF仅在流的末尾或出现错误时返回。 所以在你的情况下发生的事情是,第一次调用fscanf()会一直读到文件的末尾,寻找匹配的字符串,然后返回0以告知你没有找到匹配项。 然后第二个调用返回EOF,因为已读取整个文件。

最后,请注意%s scanf格式运算符仅捕获到下一个空白字符,因此在任何情况下都不需要排除\ n或\ r \ n。

有关更多信息,请参阅fscanf文档: http : //www.cplusplus.com/reference/clibrary/cstdio/fscanf/

你的循环有几个问题。 你写了:

 while( fscanf( f, "%[^\n\r]s", cLine ) != EOF ) /* do something */; 

有些事情需要考虑:

  1. fscanf()返回存储的项目数。 如果它读取超过文件末尾或文件句柄有错误,它可以返回EOF。 您需要区分有效的零返回值,在这种情况下,缓冲区cLine没有成功读取的新内容。

  2. 如果发生匹配失败,则会出现问题,因为很难预测文件句柄现在指向流中的位置。 这使得从失败的匹配中恢复比预期的更难。

  3. 你写的模式可能不符合你的意图。 它匹配任何数量的非CR或LF的字符,然后期望找到文字s

  4. 您没有保护缓冲区免受溢出。 无论分配给该缓冲区的大小如何,都可以从文件中读取任何数量的字符并将其写入缓冲区。 这是一个不幸的常见错误,在许多情况下,攻击者可以利用它来运行攻击者选择的任意代码。

  5. 除非您特别要求以二进制模式打开f否则行结束转换将在库中发生,您通常不会看到CR字符,通常也不会出现在文本文件中。

您可能想要一个更像下面的循环:

 while(fgets(cLine, N_CLINE, f)) { /* do something */ ; } 

其中N_CLINE是启动cLine的缓冲区中可用的字节数。

fgets()函数是从文件中读取一行的首选方法。 它的第二个参数是缓冲区的大小,它从文件到缓冲区读取的字节数小于1。 它总是以nul字符终止缓冲区,以便可以安全地传递给其他C字符串函数。

它在文件末尾,换行符或读取的buffer_size-1字节的第一个buffer_size-1点停止。

它将换行符留在缓冲区中,这一事实允许您区分比缓冲区长的单行和短于缓冲区的行。

如果由于文件结束或错误而没有复制字节,则返回NULL,否则返回指向缓冲区的指针。 您可能希望使用feof()和/或ferror()来区分这些情况。

我认为这段代码的问题是因为当你用%[^ \ n \ r] s读取时,事实上,你读到“\ n”或“\ r”,但你没有读到’\ n ‘或’\ r’也。 因此,在循环再次使用fscanf读取之前,需要获取此字符。 做那样的事情:

 do{ fscanf(f, "%[^\n\r]s", cLine) != EOF /* Do something here */ }while(fgetc(file) != EOF)