ungetc()保证在scanf(“...%c”)之后工作吗?

时间:2013-05-07 12:16:36

标签: c

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

3 个答案:

答案 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 始终做正确的事情,但在这种特殊情况下它显然是正确的。