clearerr用于什么?

7
我正在尝试理解 stdio 函数 clearerr() 应该在什么情况下使用。
例如,如果我在有效的 FILE* 上进行 fread() 或 fwrite() 操作,并得到一个短计数并且 ferror 为 true,那么我该怎么办?
从我到目前为止所读到的内容来看,fread() 和 fwrite() 都是强大的函数,并且会阻塞和/或重试(如果在较低级别函数中发生锁定和/或中断),因此似乎没有任何必要使用 clearerr(),因为 fread 或 fwrite 错误将是如此灾难性的,以至于没有必要尝试恢复。
此外,ferror() 只告诉我有错误发生,而不是错误是什么。
   #define SZ 1024
   FILE* fp = fopen( "foo", "r" );
   if ( fp ) {
      char b[SZ];
      int ch_count = fread( b, sizeof(char), SZ, fp );
      if ( ch_count != SZ && ferror( fp ) ) {
          // how would clearerr() be used. I don't know? 
          // ....
          // should I drop through here to fclose? (when I've got an ferror)
      }
      fclose( fp );
   }

1
如果用户键入Control-D(或在Windows上键入Control-Z),则您的程序会将其视为EOF,但是如果您想继续读取,则通常必须在stdin上调用“clearerr”。 - Steve Summit
clearerr()确实很少使用,基本上就像你所描述的那样。我怀疑它的用途在过去更为普遍,因为I/O设备可能会出现问题,需要手动操作员进行干预解决。但即便如此,今天仍然有一些使用情况。 - John Bollinger
4个回答

9

至少有一个真实的使用场景需要使用clearerr函数:当你想要在不以独占模式打开文件的情况下模拟tail -f功能时。也就是说,另一个或多个进程在文件末尾写入内容,而一个进程重复读取已经到达了文件结尾的文件,以查看是否有新数据到达。在这种情况下,代码可能会像这样:

for (;;) {
    if (NULL == fgets(line, sizeof(line), fd)) {
        sleep(n);
        clearerr(fd);     // reset EOF condition
    }
    else {
        fputs(line, fdout);
    }
}

这需要一个 feof() 测试来区分 EOF 情况和 ferror() 情况吗?如果 NULL 返回是由于错误而不是 EOF,我就不知道该怎么办了。 - Kelvin
@TobySpeight:糟糕,我忘记了“-f”。谢谢你的提醒!帖子已编辑。 - Serge Ballesta

3

设置FILE的错误状态(由ferror报告)的函数,即使稍后成功调用,也不会清除它。同样,如果在读取时遇到文件结尾,即使文件后来有更多数据可用,也不会自动清除。

基本上这意味着,如果您正在使用ferror检查错误状态,并且有某种从中恢复的方法,则ferror将一直指示错误,直到您使用clearerr

在您的示例中,如果您只使用fread的返回值作为终止读取的条件(即EOF和任何类型的错误都被视为最终),则无需clearerr:只需继续执行fclose(并可能使用ferror确定是否打印错误消息)。

另一方面,如果FILE实际上是可以稍后读取成功的流,并且您检测(或假定)了特定条件并重试,则应在重试之前clearerr,否则将在以后的尝试中继续看到旧的错误条件。

同样,正如评论中指出的那样,clearerr还清除了文件末尾状态,因此在使用feof检查文件末尾时也适用。(注意,当读取时通常不应该使用!feof(file)作为循环条件。)

3
重要澄清:clearerr() 函数清除错误标志和文件结束标志。后者比前者更有用。 - John Bollinger

2

clearerr()函数可以清除流的错误和EOF标记。

假设FILE如下:


```html Original Answer ```
typedef struct {
    int fd;
    char *buf;
    int error;
    int eof;
} FILE;
FILE *file;

这将把file->errorfile->eof设置为0。这样做的一些原因包括文件I/O,例如当一个文件给出EOF,但另一个程序(或另一个线程等)附加到它时。如果你在这样做后清除错误,你的程序可以充当类似于tail -f的替代品。"最初的回答"

1
FILE 的错误指示器和文件结束指示器在逻辑上是分开的。然而,clearerr() 会同时清除它们两个。 - John Bollinger

2

clearerr()函数清除错误和文件结束标志。

一个严谨使用clearerr()的例子:

// Return -1 on end-of-file
// Return -2 on rare file error
// Else return the unsigned char value
int my_get_quandry() {
  // At this point, the file end-of-file flag may be set.
  // At this point, the file file error flag may be set.
  // They may both be set.

  // Attempt to read another
  int ch = fgetc();
  if (ch != EOF) {
    return ch;
  }
  // Now was the EOF due to a end-of file or error?
  // feof() is true if end-of-file just occurred OR if end-of-file was set earlier
  // ferror() is true if error just occurred OR if error was set earlier
  // If only one feof() or ferror() is true, we know  why EOF just occurred,
  // Yet if both set, we do not know.
  ...?
}

Use clearerr()

// Return -1 on end-of-file
// Return -2 on rare file error
// Else return the unsigned char value
int my_get_crystal() {
  clearerr(stdin);

  // Attempt to read another
  int ch = fgetc();
  if (ch != EOF) {
    return ch;
  }
  // Now EOF due to at most one reason
  if (feof(stdin)) return -1;
  if (ferror(stdin)) return -2;

  // if code reaches this point, it is due to the odd-ball platform of `char` having the
  // same range as `int`.  But let us leave that platform for another day.
  return ch;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接