检查 fgetc() 的 EOF 和错误的更好方法是什么?

What is the better way to check EOF and error of fgetc()?

我一直使用这种方法

int c;
while ((c = fgetc(fp))!=EOF)
{
    printf("%c", c);
}

因为在我看来它更具可读性和健壮性。但是对于我的回答 , 评论说

if ( feof(fp) ) is more robust than int c; while ((c = fgetc(fp))!=EOF)

作为

    while(1)
    {
        c = fgetc(fp);
        if ( feof(fp) )
        {
            break ;
        }
        printf("%c", c);
    }

比第一个版本更健壮。那么我应该使用什么版本呢?请解释一下为什么那个版本更好。

编辑

在问题Why is “while ( !feof (file) )” always wrong?中问到为什么控制循环中的feof()总是错误的。但是以正确的方式在 if 条件下检查 feof() 总是错误的?解释是可观的。

我通常这样编写输入循环:

int c;

while (c = fgetc(fp), c != EOF) {
    /* do something with c here */
}

/* check if EOF came from an end-of-file or an error */
if (ferror(fp)) {
    /* error handling here */
}

您通常不应使用这样的循环条件:

while (!feof(fp)) {
    /* do stuff */
}

for (;;) {
    c = fgetc(fp);
    if (feof(fp))
        break;
}

因为遇到IO错误时会中断。在这种情况下,fgetc returns EOF 但未设置文件结束标志。您的代码可能会进入无限循环,因为错误情况通常会持续存在,直到采取外部操作为止。

正确的方法是检查fgetc()的结果:如果它等于EOF,你通常可以停止读取更多的数据,因为在IO错误和结束的情况下-文件条件,通常无法读取更多数据。然后您应该检查是否发生错误并采取适当的措施。

建议的改进并没有更好,甚至更不稳健。

here所述,如果发生读取错误(没有eof),它会进入无限循环。在这种情况下,feof 会 return 0fgetc return 会 EOF.

你的版本没有这个问题

此外,您的版本更短、更简单并且非常标准。

2 个有趣的问题

ferror()

ferror() 反映了流 的 错误指示器的状态。当出现罕见的输入错误时设置此标志 并保持设置 直到被清除 - 参见 clearerr()。如果a read输入错误,后面代码再次读取,没有清除ferror()仍然报true,即使后面的read没有错误。

fgetc() returns EOF 时,可能是由于文件结束(常见)​​或罕见的输入错误。最好检查 feof() 而不是 ferror() 来区分。 ferror() 由于先前的错误而不是当前的情况可能是真的 - 这肯定是文件结束。

int c;
c = fgetc(file);
if (c == EOF) {
  if (feof(file)) puts("end-of-file");
  else puts("input error");
}

Wide char:由于 C 中的 出现了测试错误条件的问题。

fgetc() returns 一个 int。它的值在 unsigned charEOF 范围内(一些负数)。

int ch;
while ((ch = fgetc(fp)) != EOF) {
  // do something with ch
}
if (ferror(fp)) Handle_InputError();
if (feof(fp)) Handle_EndOffFile();  // Usually nothing special

然而 C 允许 unsigned char 的范围比 int 的正数更宽。将 unsigned char 转换为 int 具有实现定义的行为,这可能导致 unsigned char 值被转换为负数 int - 并且匹配 EOF .

这样的平台很少见,不属于2015年的主流。大多数都会有UCHAR_MAX <= INT_MAX,通常使用上面的样式。令人怀疑的是,这些平台是否会变得普遍,因为像上面这样的代码数量依赖于 EOF 不同于 unsigned char 转换为 int

如果代码需要处理 UCHAR_MAX > INT_MAX 的罕见情况,那么

int c;
for (;;)
{
    c = fgetc(file);
    if (c == EOF) {
      if (feof(file)) break;
      if (ferror(file)) break;
      // fall through if both if's fail.
    }
    // do stuff with c
}

while ( !feof (file) ) always wrong? 中的热门参考强调了代码在检查问题之前使用 fgetc(in) 的结果时经常犯的错误。上面的两个代码在使用 fgetc().

的结果之前检查错误条件

第二个代码处理所有情况,包括可能只适用于位于某个早已被遗忘的垃圾堆中的计算机的情况。第一种更为常见。