使用fscanf读取字符串和空行

时间:2017-10-09 23:28:53

标签: c scanf

我有一个包含关键字和整数的文本文件,并且可以访问文件流以解析此文件。

我可以通过做解析来解析它 while( fscanf(stream, "%s", word) != -1 )获取文件中的每个单词和int以供我解析,但我遇到的问题是我无法检测到空行“\ n”,然后我需要检测某些内容。我可以看到\ n是%s未检测到的字符。如何修改fscanf以获取EOL字符?

1 个答案:

答案 0 :(得分:1)

您可以使用fscanf完成您想要做的事情,但是正确执行此操作所需的检查和验证的数量,与使用正确的面向行的输入函数,如fgets

使用fgets(或POSIX getline)检测空行不需要任何特殊内容,或者除了读取法线之外。例如,要使用fgets阅读一行文字,您只需提供一个足够大小的缓冲区,然后拨打一个电话即可将'\n'读入并包含buf

while (fgets (buf, BUFSZ, fp)) {        /* read each line in file */

要检查该行是否为空行,只需检查buf中的第一个字符是否为'\n'字符,例如

    if (*buf == '\n')
        /* handle blank line */

或者,在正常情况下,您将通过获取长度并使用 nul-terminated \n'来移除尾随&{39; '\n' >性格。在这种情况下,您只需检查长度是否为0(删除后),例如

    size_t len = strlen (buf);          /* get buf length */
    if (len && buf[len-1] == '\n')      /* check last char is '\n' */
        buf[--len] = 0;                 /* overwrite with nul-character */

注意:如果最后一个字符不是'\n',您知道该行长于缓冲区并且该行中的字符仍然未读 - 并且将在下一行中读取调用fgets,或者您已经到达文件的末尾,并在最后一行结束非POSIX行)

总而言之,使用fgets识别空行并提供打印完整行的示例,即使行超过缓冲区长度,也可以执行以下操作:

#include <stdio.h>
#include <string.h>

#define BUFSZ 4096

int main (int argc, char **argv) {

    size_t n = 1;
    char buf[BUFSZ] = "";
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    while (fgets (buf, BUFSZ, fp)) {        /* read each line in file */
        size_t len = strlen (buf);          /* get buf length */
        if (len && buf[len-1] == '\n')      /* check last char is '\n' */
            buf[--len] = 0;                 /* overwrite with nul-character */
        else {   /* line too long or non-POSIX file end, handle as required */
            printf ("line[%2zu] : %s\n", n, buf);
            continue;
        }   /* output line (or "empty" if line was empty) */
        printf ("line[%2zu] : %s\n", n++, len ? buf : "empty");
    }
    if (fp != stdin) fclose (fp);           /* close file if not stdin */

    return 0;
}

示例输入文件

$ cat ../dat/captnjack2.txt
This is a tale

Of Captain Jack Sparrow

A Pirate So Brave

On the Seven Seas.

示例使用/输出

$ ./bin/fgetsblankln ../dat/captnjack2.txt
line[ 1] : This is a tale
line[ 2] : empty
line[ 3] : Of Captain Jack Sparrow
line[ 4] : empty
line[ 5] : A Pirate So Brave
line[ 6] : empty
line[ 7] : On the Seven Seas.

那为什么每个人都推荐fgets

好吧,让我们看看fscanf做同样的事情,我会让你成为法官。首先,fscanf不会使用'\n' 格式说明符(默认情况下)或使用字符类时读取或包含尾随的"%s" "%[^\n]"(因为它被明确排除在外)。因此,您无法使用相同的格式字符串读取带有字符的(1)行和(2)没有字符的行。您可以阅读字符并fscanf成功,或者您没有遇到匹配失败

正如评论中提到的那样,如果输入缓冲区中的下一个字符是使用'\n'fgetc的{​​{1}}字符,则必须预先检查 1}})然后将其放回输入缓冲区getc,如果它不是。

进一步添加到ungetc任务,您必须独立验证每个检查,放回并阅读整个过程中的每一步。这会导致大量检查以处理所有情况,并提供必要的所有检查以避免未定义的行为

作为这些检查的一部分,您需要将读取的字符数限制为少于缓冲区中字符数的一个,同时捕获下一个字符以确定该行是否太长而不适合。需要进行额外的检查以处理(没有失败)最后一行上具有非POSIX行结尾的文件 - fscanf处理的问题没有问题。

以下是与上述fgets代码类似的实现。通过并理解为什么每个步骤都必要以及每个验证可以防止的步骤。您可以稍微重新排列,但它已被削减到接近最低限度。经过它后,应该清楚为什么fgets是处理空行检查的首选方法(通常是面向行的输入)

fgets

使用/输出与上述相同。仔细看看,如果您有任何其他问题,请告诉我。