我猜,ungetc()可能在scanf(“%d”)之后失败,因为scanf可能会隐式调用ungetc()作为第一个非数字字符。
但如果格式以%c结尾,这不会发生,对吗?
答案 0 :(得分:1)
如果您阅读source code for scanf(实际上,操作scanf
的内部函数),您将看到%c
说明符将触发执行CT_CHAR
开关案例,它不会在标准输入上调用ungetc
。所以你是对的。
这仅适用于GNU libc,但我希望其他实现的行为类似。
答案 1 :(得分:0)
当然,它并不仅仅取决于格式是否以%c结束,因为之前的转换说明符可能会失败。
答案 2 :(得分:0)
glibc的其他答案是正确的。不幸的是,从标准的角度来看,这是一个难以回答的问题。
POSIX指定fscanf(3)
和ungetc(3)
,但它没有描述他们的互动。它有关于前者的说法:
除非转换规范包含
n
转换说明符,否则应从输入中读取项目。输入项应定义为输入字节的最长序列(直到任何指定的最大字段宽度,可以用字符或字节来衡量,取决于转换说明符),它是匹配序列的初始子序列。 输入项目后的第一个字节(如果有)将保持未读状态。
整篇文档中唯一提及ungetc
与OP的问题无关。但是,它确实确认fscanf
意味着返回通过ungetc
推回的字符。这并不是一个严重的疑问,但ungetc
从未定义“读取操作”的含义,因此在标准中指定一个特定的行是有帮助的。
标准没有规定“第一个字节[...]仍然未读”的含义。我认为它意味着C库需要表现as if字符未被读取,即使任何实现必须读取字符以知道输入项何时结束。反过来,这意味着它不应该阻止使用ungetc
,无论格式字符串如何。
(虽然这个读数可能看起来很紧张,但POSIX肯定不会说“第一个字节[...]将保持未读或被推入输入流,就好像是通过ungetc()”,如果他们想在ungetc
电话后允许fscanf
失败,那么他们就会写这些内容。)
幸运的是,ungetc
的规范很宽松,实际上可以正常工作。 POSIX允许多个字节的回推,甚至指定在这种情况下会发生什么:
推回后的字节应由该流的后续读取按其推送的相反顺序返回 [...]
应提供一个字节的回推。如果在同一个流上调用ungetc()太多次而没有对该流进行干预读取或文件定位操作,则操作可能失败。
它还方便地没有指定实现必须始终提供相同数量的字节的回送,只是它们在第一个之后被允许失败。这意味着一致的实现可以简单地提供两个字节的ungetc
推回,只要fscanf
只使用一个。
理论上,我认为标准实际上要求在ungetc
或其姐妹函数调用后fscanf
可用fscanf
。 #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 2.21的Ubuntu变体:
data.table
这并不能保证glibc 始终做正确的事情,但在这种特殊情况下它显然是正确的。