使用fscanf()与fgets()和sscanf()

在实用C编程一书中,我发现fgets()sscanf()的组合用于读取输入。 但是,在我看来,只使用fscanf()函数可以更轻松地实现相同的目标:

从书中(想法,而不是例子):

 int main() { int age, weight; printf("Enter age and weight: "); char line[20]; fgets(line, sizeof(line), stdin); sscanf(line, "%d %d", &age, &weight); printf("\nYou entered: %d %d\n", age, weight); return 0; } 

我认为它应该是:

 int main() { int age, weight; printf("Enter age and weight: "); fscanf(stdin, "%d %d", &age, &weight); printf("\nYou entered: %d %d\n", age, weight); return 0; } 

或者有一些隐藏的怪癖我不见了?

这两种方法存在一些行为差异。 如果使用fgets() + sscanf() ,则必须在同一行上输入两个值,而stdin上的fscanf() (或等效地, scanf() )如果找不到第二个值,则会从不同的行读取它们在您输入的第一行。

但是,最重要的差异可能与处理错误情况以及面向行的输入和面向字段的输入的混合有关。

如果您在使用fgets()读取后无法使用sscanf()解析的行,您的程序可以简单地丢弃该行并继续前进。 但是,当fscanf()无法转换字段时,会在流上留下所有输入。 因此,如果您未能阅读所需的输入,则必须自己阅读所有想要忽略的数据。

如果你想在你的代码中混合面向字段(ala scanf() )和面向行的(例如fgets() )调用,那么另一个微妙的问题就出现了。 例如,当scanf()转换一个int ,它将在输入流上留下一个\n (假设有一个,如同按下回车键),这将导致后续调用fgets()立即返回输入中的那个字符。 对于新程序员来说,这是一个非常普遍的问题。

所以,虽然你是正确的,你可以像这样使用fscanf() ,你可以通过使用fgets() + sscanf()来避免一些麻烦。

仅使用fscanf()的问题主要是在错误管理中。

想象一下,你为这两个节目输入了“ 51岁,85公斤 ”。

第一个程序在sscanf()失败,你仍然sscanf()向用户报告错误,尝试不同的解析替代方案;

第二个程序多年失败, age可用, weight无法使用。

请记住始终检查*scanf()的返回值以进行错误检查。

  fgets(line, sizeof(line), stdin); if (sscanf(line, "%d%d", &age, &weight) != 2) /* error with input */; 

编辑

使用第一个程序,错误发生后,输入缓冲区清零; 使用第二个程序,输入缓冲区以YEAR开头…

第一种情况下的恢复很容易; 在第二种情况下的恢复必须通过某种清除输入缓冲区。

在以下情况下, fscanf()fgets() / sscanf()之间没有区别:

  1. 输入数据格式正确。

出现两种类型的错误:I / O和格式。 fscanf()在一个函数中同时处理这两种错误类型,但提供的恢复选项很少。 单独的fgets()sscanf()允许I / O问题与格式问题逻辑分离,从而实现更好的恢复。

  1. 只有1个解析路径。

从扫描中分离I / O允许多个sscanf()选项。 如果给定的缓冲区扫描没有实现所需的结果,则可以使用具有不同格式的其他sscanf()

  1. 没有嵌入'\0'

很少发生'\0' ,但是如果发生'\0' s '\0'sscanf()将不会看到它,因为扫描因其发生而停止,而fscanf()继续。

在所有情况下,检查所有三个function的结果。