c - 流错误指示器如何影响以下输入代码?

标签 c error-handling language-lawyer

每个流都有“一个错误指示器,用于记录是否发生读/写错误”。

它通常很少由各种函数设置:fgetc(), fflush(), fseek(), ...

它被各种函数清除:rewind(), clearerr(), fopen(), ...

int ferror(FILE *stream) 报告状态。

The ferror function returns nonzero if and only if the error indicator is set for stream.


在这种情况下,肯定是 刚刚发生了输入错误。

if (!ferror(istream)) {
  int ch = fgetc(istream);
  if (ch == EOF && ferror(istream)) {
    puts("Input error just occurred");
  }
}

深入探索 fgetc()fgetc() 不返回 EOF 因为设置了错误指示符,而是因为“如果发生读取错误”或文件结束相关的原因1。通常一旦发生错误(例如串行流上的奇偶校验错误),代码不会在不清除错误的情况下继续读取,但请考虑当它继续时会发生什么。

我看到 8 种情况:错误指示器fgetc() 之前设置/清除,fgetc() 是否返回 EOF,以及随后的 ferror() 是否为真。

int e1 = !!ferror(istream);
int eof = fgetc(istream) == EOF;
int e2 = !!ferror(istream);

假设没有 UB,8 个中有 5 个是可能的,而不是 3 个意外的吗?尤其是有效输入是否可能带有错误指示器集? 2

e1 eof e2  
0  0   0   Normal reading of valid data    
0  0   1   Unexpected
0  1   0   End-of-file
0  1   1   Input error
1  0   0   Unexpected
1  0   1   Normal reading of valid data with error indicator set!
1  1   0   Unexpected
1  1   1   Input error or end-of-file

如果在输入操作之前设置了错误指示器,事情就会变得复杂,预先清除它可以简化代码。然而,这可以防止错误指示器积累。

如果代码没有预先清除错误指示器并且想要检测输入的是否有罕见的输入错误,那么测试!feof()似乎是有意义的而不是 ferror() 来检测。

检查 ferror() 是否具有潜在误导性? 还是我遗漏了有关错误指示器的某些信息?

char buf[80];
for (int i=0; i<(80-1); i++) {
  int ch = fgetc(stdin);
  if (ch == EOF) {
    if (ferror(stdin)) {
      puts("Input error or (Prior input error and end of file occurred)");  // ambiguous
    } 
    if (feof(stdin)) { 
      puts("End of file occurred");
    } else {
      puts("Input error occurred");  // ferror() test not needed
      i = 0; // ignore prior input
    }
    break;
  }
  if (ch == '\n') break;
  buf[i++] = ch; 
}
buf[i] = 0; 

类似问题

File operations with error indicator set 。这个悬而未决的问题集中于在不测试 fgetc() 返回值的情况下累积错误指示器(答案冒险进入 errno 并制作用户错误标志),这个问题试图简单地消除 fgetc() 的歧义。

fgetc(): Is it enough to just check EOF? 没有解决在 fgetc() 之前设置的错误指示符

类似的问题适用于输出和 I/O 流,但这个问题的重点是输入流。


1 int fgetc(FILE *stream) 返回

If the end-of-file indicator for the stream is set, or if the stream is at end-of-file, the end-of-file indicator for the stream is set and the fgetc function returns EOF. Otherwise, the fgetc function returns the next character from the input stream pointed to by stream. If a read error occurs, the error indicator for the stream is set and the fgetc function returns EOF. C11dr §7.21.7.1 2

2 在案例 0-1-0、1-1-1 上。似乎 UCHAR_MAX == UINT_MAXunsigned char 可以返回并等于 EOF 而不是由于文件结尾或输入错误。

最佳答案

Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones? especially is valid input possible with error indicator set?

具体说到标准的规定,我倾向于同意你的分析:

  • 指定了几个函数来清除流的错误指示符,fgetc() 不是其中之一。更一般地说,它们都不是数据传输功能。因此,如果在将流提供给 fgetc() 进行读取之前为该流设置了错误指示器,则在该函数返回时仍应设置它,尽管有所有其他注意事项。这涵盖了这些情况:*

    1  0   0   Unexpected
    1  1   0   Unexpected
    1  1   1   Input error or end-of-file
    

    它还涵盖了与错误指示器的预期值有关的这种情况,尽管它没有说明它是否真的会发生:

    1  0   1   Normal reading of valid data with error indicator set!
    
  • fgetc() 被指定为返回 EOF。因此,如果 fgetc() 返回 EOF 以外的任何内容,那么它不会在该调用中设置流的错误(或文件结束)指示符。这涵盖了这些情况:

    0  0   0   Normal reading of valid data    
    0  0   1   Unexpected
    

    另一方面,如果 fgetc() 确实 返回 EOF 则流的文件结束指示符或其错误指示符之后应该会发现集合。但标准对这些情况进行了区分,并指定用户可以通过 feof()ferror() 函数来区分它们。这涵盖了这些情况:*

    0  1   0   End-of-file
    0  1   1   Input error
    
  • 最后,我同意fgetc() 的所有行为都不以流的错误指示器的初始状态为条件。只要流最初没有定位在它的末尾,并且它的文件结束指示符最初也没有设置,“fgetc 函数返回 stream 指向的输入流中的下一个字符。 “这表明,最有趣的情况实际上是允许的:

    1  0   1   Normal reading of valid data with error indicator set!
    

    但是,这种情况在抽象上是允许的,并不意味着在实践中可以观察到。细节似乎没有具体说明,我希望它们取决于为相关流提供服务的驱动程序的实现。完全有可能一旦遇到错误,驱动程序将继续在后续读取中报告错误,直到适本地重置,甚至更长的时间。从 C 的角度来看,这将被解释为每次后续读取时发生的(附加)错误,并且语言规范中没有任何内容可以阻止这种情况。甚至没有使用清除流的错误指示器的功能之一。

If codes does not clear the error indicator before hand and wants to detect if a line of input had a rare input error, it seems to make sense to test !feof() and not ferror() to detect.

Is checking ferror() potentially misleading? or have I missed something about the error indicator?

我同意,如果一个流的错误指示器最初被设置,它的文件结束指示器没有,并且用 fgetc() 读取它返回 EOF,然后ferror() 不能有效区分文件结束和错误情况,而 feof() 应该。

另一方面,在遇到错误后是否可以有用地继续读取给定的流取决于实现,可能还取决于具体情况。即使错误指示器通过 clearerr() 调用清除,这也适用,更不用说错误指示器是否被清除。


* 虽然我同意在 UCHAR_MAX > INT_MAX 的情况下关于 EOF 存在歧义,但我断言那是这只是为什么这样的实现会出现问题的几个原因之一。因此,作为一个实际问题,我认为这些实现完全是假设性的。

关于c - 流错误指示器如何影响以下输入代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53272650/

相关文章:

即使我设置了错误处理,PHP 也不会显示 PDO 错误

python - 下订单时出现错误201交互式经纪人tws

c - 从函数和未定义行为返回局部部分初始化的结构

c - 请帮助我的代码,编译问题

C程序在Linux上转储正在运行的进程的堆栈

Java:如何处理错误的方法参数?

c++ - 指针互转换与具有相同地址

c++ - 应用间接时,标准是否要求指针变量的左值到右值转换?

c - 为什么故意使无效文件定位不会引发任何错误

c - 区分用户输入是 int 还是 char/string