6

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

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

但不知何故,我每次都只得到第一行。这是读一行的坏方法吗?我应该修复什么以使其按预期工作?

4

7 回答 7

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()似乎是您正在尝试做的事情,在字符串中读取直到遇到换行符。

于 2009-05-14T06:19:16.820 回答
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;
}

享受 :)

于 2011-06-15T11:59:07.460 回答
1

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

不要与 fscanf 战斗。只是不要使用它。曾经。

于 2009-12-08T12:29:18.060 回答
1

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

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

也就是说,通过使用fgets()尽可能多的行来读取适合缓冲区的内容,您将获得相同的结果,而且痛苦更少。

于 2009-05-14T07:27:03.787 回答
0

在我看来,您正在尝试在 fscanf 字符串中使用正则表达式运算符。该字符串 [^\n\r]对 fscanf 没有任何意义,这就是您的代码无法按预期工作的原因。

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

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

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

于 2009-05-14T06:17:32.877 回答
0

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

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读取的字节的第一个处停止。

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

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

于 2009-05-14T07:22:43.937 回答
0

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

do{
    fscanf(f, "%[^\n\r]s", cLine) != EOF

    /* Do something here */

}while(fgetc(file) != EOF)
于 2017-04-30T22:55:36.760 回答