0

我猜想,ungetc() 在 scanf("%d") 之后可能会失败,因为 scanf 可能会为第一个非数字字符隐式调用 ungetc()。
但是如果格式以 %c 结尾,这不会发生,对吧?

4

3 回答 3

1

如果您阅读scanf 的源代码(实际上是运行 的内部函数scanf),您会看到说明%c符将触发CT_CHARswitch case 的执行,它不会调用ungetc标准输入。所以你是对的。

这仅适用于 GNU libc,但我希望其他实现具有类似的行为。

于 2013-05-07T12:28:18.970 回答
1

其他答案对于 glibc 是正确的。不幸的是,从标准的角度来看,这是一个难以回答的问题。

POSIX 指定了fscanf(3)and ungetc(3),但没有描述它们的交互。它对前者有这样的说法:

除非转换规范包含转换说明符,否则应从输入中读取项目n。输入项应定义为输入字节的最长序列(直到任何指定的最大字段宽度,可以根据转换说明符以字符或字节为单位测量),它是匹配序列的初始子序列。输入项之后的第一个字节(如果有)应保持未读状态。

整个文件中唯一提到的ungetc与OP的问题无关。但是,它确实确认这fscanf意味着返回通过ungetc. 这没有严重的疑问,但从ungetc未定义“读取操作”的含义,因此在标准中有一个特定的行来指向是有帮助的。

该标准没有具体说明“第一个字节 [...] 应保持未读”的含义。我将其解释为意味着 C 库必须表现得好像没有读取字符一样,即使任何实现都必须读取字符才能知道输入项何时结束。反过来,这意味着它不应该阻止使用ungetc,无论格式字符串如何。

(虽然这个读数可能看起来很紧张,但 POSIX 肯定不会说“第一个字节 [...] 应该保持未读或被推送到输入流上,就像被 ungetc() 一样”,如果他们想要的话,这是他们会写的允许在通话ungetc后失败fscanf。)

幸运的是,ungetc的规范足够宽松,这实际上可以工作。POSIX 允许多个字节的回送,甚至指定在这种情况下会发生什么:

推回的字节应由该流上的后续读取以它们推入的相反顺序返回。
[...]
应提供一个字节的回推。如果在同一流上调用 ungetc() 次数过多而没有对该流进行干预读取或文件定位操作,则操作可能会失败。

它也方便地没有指定实现必须始终提供相同数量的回推字节,只是允许它们在第一次之后失败。这意味着一个符合要求的实现可以简单地提供两个字节的ungetc推回,只要fscanf只使用一个。

理论上,我认为该标准实际上需要在任何调用或其姊妹函数 ungetc之后可用。是一个读操作,读操作应该给你留下至少一个字节的回退。在实践中,这是对标准的非常微妙的解读,您应该期望实现在这一点上有所不同。fscanffscanf

最后,作为一个实际问题,我发现这个程序在我的系统上成功执行,它有一个 glibc 2.21 的 Ubuntu 变体:

#include <stdio.h>

int main(int argc, char **arv){
    FILE *file = fopen("/tmp/file.txt", "w+");
    if(file == NULL){
        perror("fopen");
        return 2;
    }
    if(fprintf(file, "123\n") == EOF){
        printf("fprintf failed.");
    }
    if(fseek(file, 0, SEEK_SET) == -1){
        perror("fseek");
        return 1;
    }
    if(ungetc((int)'1', file) == EOF){
        printf("First ungetc failed");
        return 3;
    }
    int value;
    if(fscanf(file, "%d", &value) == EOF){
        printf("fscanf failed");
        return 4;
    }
    if(ungetc((int)'2', file) == EOF){
        printf("Second ungetc failed");
        return 5;
    }
    return 0;
}

这并不能保证 glibc总是做正确的事情,但它显然在这种特定情况下做正确的事情。

于 2016-03-23T05:11:04.330 回答
0

当然,它并不仅仅取决于格式是否以 %c 结尾,因为在此之前的转换说明符可能会失败。

于 2014-07-03T08:25:55.373 回答