我猜,ungetc()可能会在scanf("%d")之后失败,因为scanf可能会隐式调用ungetc()作为第一个非数字字符。
但是如果格式以%c结尾,就不会发生这种情况,对吧?
发布于 2013-05-07 20:28:19
如果阅读source code for scanf (实际上是操作scanf的内部函数),您将看到%c说明符将触发CT_CHAR切换用例的执行,这不会在标准输入上调用ungetc。所以你是对的。
这只适用于GNU libc,但我希望其他实现也有类似的行为。
发布于 2016-03-23 13:11:04
对于glibc,其他答案是正确的。不幸的是,从标准的角度来看,这是一个很难回答的问题。
POSIX同时指定了fscanf(3)和ungetc(3),但没有描述它们之间的交互。关于前者,它是这样说的:
除非转换规范包含n转换说明符,否则应从输入中读取项目。输入项应定义为输入字节的最长序列(最大可达任何指定的最大字段宽度,可根据转换说明符以字符或字节进行测量),它是匹配序列的初始子序列。输入项后的第一个字节(如果有)将保持未读状态。
整篇文档中唯一提到的ungetc与OP的问题无关。但是,它确实确认了fscanf旨在返回通过ungetc推送回来的字符。这没有什么严重的疑问,但是ungetc从来没有定义过“读操作”是什么意思,所以在标准中有一个特定的行来指向是很有帮助的。
该标准没有具体说明“第一个字节...将保持未读状态”是什么意思。我将其解释为C库需要在未读取字符的情况下执行操作,即使任何实现都必须读取字符才能知道输入项何时结束。这反过来意味着,无论格式字符串如何,it都不应该阻止 ungetc,的使用。
(虽然这种读取可能看起来很紧张,但POSIX肯定不会说“第一个字节……应该保持未读状态,或者像ungetc()一样被推入到输入流中”,如果他们想让ungetc在fscanf调用后失败,就应该这样写。)
幸运的是,ungetc的规范足够宽松,可以实际工作,允许多个字节的回推,甚至指定了在这种情况下会发生什么:
推送的字节将由该流上的后续读取以与其推送相反的顺序返回。
..。
应提供一个字节的回推。如果在同一个流上多次调用ungetc(),而没有对该流执行中间读取或文件定位操作,则操作可能会失败。
它还方便地没有指定实现必须始终提供相同数量的回推,只允许它们在第一次回推之后失败。这意味着,只要fscanf只使用一个字节,符合规范的实现就可以简单地提供两个字节的ungetc回推。
从理论上讲,我认为该标准实际上要求在调用fscanf或其姊妹函数之后,ungetc必须可用。fscanf是一个读操作,读操作应该给你留下至少一个字节的回推。实际上,这是对标准的一种非常微妙的解读,您应该预料到实现在这一点上会有所不同。
最后,作为一个实际问题,我发现这个程序可以在我的系统上成功执行,我的系统有Glibc2.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总是做正确的事情,但它显然在这个特定的情况下做了正确的事情。
发布于 2014-07-03 16:25:55
当然,这并不仅仅取决于格式是否以%c结尾,因为在此之前的转换说明符可能会失败。
https://stackoverflow.com/questions/16418837
复制相似问题