这个 fscanf 行为不一致吗?

use*_*023 4 c

通常fscanf,当使用 扫描非整数时%d,将失败,直到从输入流中显式删除非整数字符。尝试扫描a123失败,直到a从输入流中删除。

尝试扫描------123失败(fscanf返回0),但-已从输入流中删除。

这是正确的行为吗fscanf

该文件包含----------123此代码的结果:

#include <stdio.h>

int main(void) {
    int number = 0;
    int result = 0;
    FILE *pf = NULL;

    if (NULL != (pf = fopen("integer.txt", "r"))) {
        while (1) {
            if (1 == (result = fscanf(pf, "%d", &number))) {
                printf("%d\n", number);
            } else {
                if (EOF == result) {
                    break;
                }
                printf("result is %d\n", result);
            }
        }
        fclose(pf);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

是:

result is 0
result is 0
result is 0
result is 0
result is 0
result is 0
result is 0
result is 0
result is 0
-123
Run Code Online (Sandbox Code Playgroud)

如果文件包含a123结果则为无限循环。

在我看来,这是不一致的行为。不?

Dev*_*lar 7

这里的重点不是不一致,而是家庭的诸多限制之一fscanf()

fscanf()该标准对于如何解析输入非常具体。从输入中一一获取字符,并根据格式字符串进行检查。如果它们匹配,则从输入中取出下一个字符。如果它们不匹配,则该字符将被“放回”,并且转换失败。

但只有最后读取的字符才会被放回。

C11 7.21.6.2 fscanf 函数,第 9 段(强调我的):

输入项被定义为最长的输入字符序列,该序列不超过任何指定的字段宽度,并且是匹配的输入序列或者是 的前缀。285)输入项之后的第一个字符(如果有)保持未读状态。

  1. fscanf 最多将一个输入字符推回输入流。因此,有些序列对strtod、strtol等可接受,但对fscanf则不可接受。

推回这一特性与保证的推回一特性无关ungetc()——它是独立的并且除此之外。(用户可能会fscanf()失败,然后是ungetc()一个字符,并期望ungetc()'d 字符从输入中出现,然后是失败的推回的字符fscanf()。*库函数可能不会调用ungetc(),这是为用户保留的。)

这使得扫描的实施变得fscanf()更加容易,但也会在某些字符序列的中间fscanf()失败,而不会实际回溯到开始转换尝试的位置。

对于您的情况,"--123"请阅读"%d"

  • 取第一个'-'。符号。一切都好,继续。
  • 取第二个'-'。匹配错误。
  • 把最后的放回去'-''-'无法按照上述方式放回第二个。
  • 返回0(转换失败)。

这是您不应该在可能存在格式错误的输入上使用的原因(之一)*scanf():扫描可能会失败,而您不知道它到底在哪失败,也没有正确回滚。

这也是该标准的一个阴暗角落,我上次检查时在许多主流库实现中实际上并未正确实现。(当我刚刚重新检查时,情况并非如此。);-)


不对可能格式错误的输入使用的其他原因fscanf()包括但不限于根本没有妥善处理数字溢出。

的预期用途fscanf()是扫描已知的格式良好的数据,最好是由同一程序使用fprintf(). 它不太适合解析用户输入。

因此,通常的建议是使用 读取整行输入,然后使用等fgets()解析内存中的行,这可以并且将以明确定义的方式处理类似上述的事情。strtol()strtod()