【问题标题】:How to check matching failures with fscanf in C如何在 C 中使用 fscanf 检查匹配失败
【发布时间】:2018-08-08 15:42:26
【问题描述】:

我希望能够在不停止读取的情况下确定我正在读取的文件是否包含错误类型的数据。

例如:这里我想从文件中存储一些整数,但如果我的文件中有一些字符,它将停止循环。我不知道如何避免这种情况以及如何返回匹配类型的错误。

int main (void) {
    FILE* input_file = fopen("toto.txt", "r");

    int a [10];

    if (input_file != NULL) {
        int i =0;
        while (fscanf(input_file, "%d", &a[i]) == 1) {
            //do something
        }
        fclose(input_file);
    }
    else {
        printf ("open failed.");
        }
    return 0;
}

【问题讨论】:

  • 编写自己的解析器。逐字符读取文件,检查所有条件,使用strtoll将字符串转换为数字,
  • fscanf(input_file, "%d %d", &a[i]) 是未定义的行为 - 您有两个格式说明符,但只有一个参数。
  • @AndrewHenle 编辑,我的错误
  • 谢谢。我会在这里回应@KamilCuk 的评论:你真的不能使用scanf() 系列中的任何一个来可靠地解析可能错误的数据。 scanf 中的 f 本身来自“格式化” - 如“扫描格式化数据”。这些函数假定输入数据已经匹配一种非常严格的格式,并且它们确实会以不可预知的方式在意外数据类型上严重失败。
  • "但如果我的文件中有一些字符,它将停止循环。" --> 清楚地说明当这种情况发生时你希望代码做什么。忽略字符?

标签: c error-handling scanf


【解决方案1】:

如果 scanf 无法使用格式项转换输入,它会返回而不消耗导致错误的字符。返回值要么是成功转换的次数,要么是EOF(如果遇到文件结尾(或读取错误)但没有成功转换)。

如果你的格式只有一个转换项,那么可能的返回值只有三个:

EOF  End of input (or read error)
0    Invalid character
1    Succesful conversion

第二种情况,如果你想继续解析,你需要消耗错误的字符,你可以用scanf("%*c");来做。

scanf 不是解析输入的非常精确的工具。比如它不会区分

223abc

223 abc

两者都将被视为成功的%d 转换然后不成功;没有办法判断是否有空格。

如果您对各种限制没问题,请继续使用它,但如果您想进行真正的输入验证,您最终会碰壁,唯一的解决方案就是“使用不同的工具”。

【讨论】:

  • 我不确定如何使用scanf("*c"); 来使用错误字符。你能再解释一下吗?
  • @djadjik:是"%*c"(感谢 Jonathan Leffler 的编辑)。这恰好匹配一个字符 (%c) 并丢弃它 (*)。见man scanf
【解决方案2】:

如果你想处理错误,你可以看一个这样的结构:

    int i = 0;
    int rc;
    while ((rc = fscanf(input_file, "%d", &a[i])) != EOF)
    {
        if (rc == 1)
        {
            …success…
            i++;
        }
        else
        {
            …deal with error…
        }
    }

通常,我鼓励在循环条件中使用fscanf(…) != 1;如果出现问题,它会停止循环。但是,如果要处理错误并继续循环,则此条件更合适。如何处理错误取决于您。您可以跳过并忽略(或报告)直到下一个空格或下一个换行符的字符。或者您可能会在文本中查找与数字不匹配的命令或其他信息。

您应该考虑的一个选项是使用 fgets() 或 POSIX getline()读取一行数据,然后使用sscanf()解析该行。这种方法至少有两个主要优点:

  • 您可以报告导致错误的整行,而不仅仅是fscanf() 部分消耗它时留下的残骸。
  • 您可以以多种不同的方式解析相同的数据,这很有优势。

【讨论】:

  • 我也想过像你这样的结构,但是当发生读取错误时我们陷入了无限循环......我会尝试你的第二个选项。
猜你喜欢
  • 2014-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-28
  • 2015-07-06
  • 1970-01-01
  • 2011-08-13
相关资源
最近更新 更多