【问题标题】:Trouble reading a line using fscanf()使用 fscanf() 读取一行时遇到问题
【发布时间】:2017-12-04 12:12:24
【问题描述】:

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

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

但不知何故,我每次都只得到第一行。这是读一行的坏方法吗?我应该如何解决才能使其按预期工作?

【问题讨论】:

    标签: c file input std


    【解决方案1】:

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

    【讨论】:

    • 如何使用 sscanf 函数读取一行(BTY 是 1024 一行的大小?)谢谢!
    • fgets 读取一行“或更少”。 fgets(buffer, 1024, file) 将读取一行,与文件中的一样多,或 1024 个字符。如果你读了一整行,那么 buffer[strlen(buffer)] == '\n'。如果到达 EOF,则返回 null,否则,该行还有更多文本。
    【解决方案2】:

    如果您想逐行读取文件(这里,行分隔符 == '\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;
    }
    

    享受:)

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 这仍然会给他留下原来的问题,即只阅读第一行。最好是 "%27[^\n\r]%*[\n\r]" 这样不匹配的字符就会被消耗掉。
      【解决方案4】:

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

      不要与 fscanf 对抗。只是不要使用它。永远。

      【讨论】:

        【解决方案5】:

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

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

        最后,请注意 %s scanf 格式操作符只捕获到下一个空白字符,所以在任何情况下都不需要排除 \n 或 \r。

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

        【讨论】:

        • [^a-z] 实际上排除了 scanf 中的 a-z。虽然,如上所述的字符串,寻找“一对字符,第一个不是换行符,第二个是一个 s”
        • cplusplus.com 上的 fscanf 文档不完整。谷歌“fscanf 扫描集”。
        【解决方案6】:

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

        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() 来区分这些情况。

        【讨论】:

        • 谢谢,我这样做了,但我想知道如果我的行大于我设置的大小,它会削减下一行的一部分还是会导致任何其他问题
        • 如果输入行长于传递给 fgets() 的缓冲区,它将在输入行结束之前停止读取,并为您提供到目前为止在缓冲区中读取的内容。您知道发生这种情况是因为缓冲区末尾没有 \n 。对 fgets() 的每次调用都将继续读取,因此您可以通过循环处理一个长行,一次处理一个缓冲区,直到缓冲区以 \n 结尾。唯一的问题是当输入在任意位置被破坏时,如何合理地解析它。
        【解决方案7】:

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

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

        【讨论】:

          猜你喜欢
          • 2015-08-04
          • 1970-01-01
          • 2010-11-06
          • 1970-01-01
          • 2015-10-19
          • 1970-01-01
          • 2015-02-19
          • 1970-01-01
          相关资源
          最近更新 更多