【问题标题】:Does fgetc return EOF on every call after end-of-file reached?到达文件结尾后,fgetc 是否在每次调用时都返回 EOF?
【发布时间】:2017-03-17 03:21:16
【问题描述】:

给定以下 C 代码:

int eofCount = 0;
while (true) {
    int c = fgetc(stdin); 
    if (c == EOF) eofCount++;
}

eofCount 会变得大于 1 吗?

我在 C 文档中找不到任何描述在达到 EOF 后 fgetc 会发生什么的任何内容。我知道我可以自己做这个簿记,但如果 stdlib 能为我做这件事,那就太好了。

我不是在寻找代码 sn-ps,因为我已经用 glibc 尝试过这个,实际上 eofCount 是在 EOF 之后递增的。我想要确认这是已定义行为的 stdlib 源代码参考或规范。依赖未定义的行为可能会导致问题。

【问题讨论】:

  • 它可能确实如此,但假设它可能并不安全。你能多谈谈你真正想做的事情吗?
  • 一些基本的流处理。我有自己的库,它接受文件指针并做一些基本的事情,我想知道这个库的调用者是否需要注意不要用 EOF 文件调用它。我同意这看起来像是未定义的行为,但我想知道是否有人知道得更好。
  • 您似乎没有很好地搜索或阅读。标准和 POSIX 手册页对行为非常清楚。
  • Glibc bug 1190 该错误是在 2005 年提交的,尽管其行为比那要早得多。不是一个重复的问题,而是高度相关的答案:stackoverflow.com/questions/19889296/… 我对另一个类似问题的看法:stackoverflow.com/questions/32322792/…
  • @user902384:您认为这里涉及哪些不同的标准?只有一个 C 标准。这对行为非常清楚。你读过吗?

标签: c libc


【解决方案1】:

只要您不是从交互式 Linux 终端读取数据(即您没有从其他文件/进程打开文件或管道标准输入),一旦您读取 EOF,所有未来读取也将读取 EOF。

如果您从 Linux 终端读取,按 EOF 的键序列(Linux 上的 CTRL-D)将读取为 EOF,但您仍然可以输入更多字符并读取它们。

来自C standard 的第 7.21.7.1 节:

3 如果设置了流的文件结束指示符,或者如果流处于文件结束位置,则流的文件结束指示符为 设置并且fgetc 函数返回EOF。否则,fgetc 函数从指向的输入流中返回下一个字符 通过流。如果发生读取错误,则错误指示 流被设置,fgetc 函数返回EOF

【讨论】:

【解决方案2】:

在到达文件结尾后,fgetc 是否在每次调用时都返回 EOF?

这取决于 2 个指标和 I/O 函数调用。


虽然 OP 没有提到,但有两个原因导致 fgetc(stdin); 返回 EOF 以及它们如何影响后续 fgetc() 调用不是对称的。此外,各种 I/O 函数的使用会影响 2 个指标,这些指标又会影响后续的fgetc() 调用

文件结束。
输入错误。


C 规范在 文件结束指示符 上是明确的,导致后续的EOF

如果未设置 stream 指向的输入流的文件结束指示符并且存在下一个字符,则 fgetc 函数获取该字符 ... C11 §7.21.7.1 2

当文件结束发生或已经发生时,会设置一个持久标志:文件结束指示符,因此对fgetc() 的后续调用将返回EOF

如果设置了流的文件结束指示符,如果流处于文件结束状态,则设置流的文件结束指示符并且fgetc 函数返回EOF ... §7.21.7.1 3


当发生罕见的输入错误时,fgetc() 返回EOF,但该事件不会设置文件结束指示符,而是设置错误指示符已设置。即使设置了错误指示符,后续调用也不一定会返回EOF。 IMO,C 规范在这一点上不够明确。

如果发生读取错误,则设置流的错误指示符,fgetc 函数返回 EOF §7.21.7.1 3


feof()ferror() 的调用可用于区分导致EOF 的原因,但也可能反映之前的I/O 活动。因此,好的代码会在返回 EOF 后立即检查这些函数,并在发生后续 I/O 时清除它们。


文件结束指示错误指示可以用void clearerr(FILE *stream);清除

rewind() 函数清除错误指示器

ungetc() 将清除文件结束指示符

其他 I/O 功能也会影响这些指标。


如果导致第一个EOF 的条件被删除并且相应的指示符被清除,则对fgetc() 的后续调用可能不会返回EOF

【讨论】:

  • 我认为标准并不清楚错误情况。如果遇到读取错误,则设置错误标志。但是随后的调用可能没有错误,因此它返回下一个字符。也就是说,在fgetc(...) == EOF 之后检查错误是一种很好的做法。 feof要慎重对待;它的设计……有问题。
【解决方案3】:

是的,计数将大于一,因为您有一个无限的while 循环。你会发现http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdfhttp://pubs.opengroup.org/onlinepubs/9699919799/ 很有用。

试试这个:

#include <stdio.h>
#include <stdbool.h>

int main() {
    int eofCount = 0;
    /*while (true) {*/

    int c = fgetc(stdin); 
    if (c == EOF) eofCount++;
        c = fgetc(stdin); 
    if (c == EOF) eofCount++;
        c = fgetc(stdin); 
    if (c == EOF) eofCount++;

    /*}*/

printf("%d\n", eofCount);
return 0;

}

编译并运行。在 Linux 上按 ctrl+D 两次,然后输入。您将获得 2 个。

这是使用 gcc -ansi -Wall -Werror -pedantic test.c 编译的,因此它满足 ANSI 即 C89 标准。

【讨论】:

  • 这不是在回答关于 EOF 的问题。如果if 条件不是每次都评估为真,那么无限循环是无关紧要的——这正是问题真正要问的。
  • Will eofCount ever become greater than 1? 这是带有“?”的问题
  • 阅读整个问题。上下文很重要。
  • @kaylum 对不起,我很匆忙。我已经更新了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多