7

I'm trying to understand when the stdio function clearerr() should be used.

For example, if I fread() or fwrite() on a valid FILE* and get a short count and ferror is true, what can I do?

From what I've read so far, fread() and fwrite() are robust and will block and/or retry (if there are locks and/or interrupts which could happen in lower level functions) so there never seems any point in using clearerr() because fread or fwrite errors will be so catastrophic there is no point in trying to recover.

Additionally, ferror() only tells me that there is an error, not what the error is.

   #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 );
   }
S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Kelvin
  • 85
  • 6
  • 1
    If the user types control-D (or control-Z on Windows), your program will read that as an EOF, but if you want to keep reading, you usually have to call `clearerr` on stdin. – Steve Summit Jun 25 '19 at 14:16
  • `clearerr()` is indeed rarely used, for pretty much the reasons you describe. I suspect that uses for it were more common in the past, with I/O devices that were subject to issues that could be resolved by manual operator intervention. But even so, there are still some use cases today. – John Bollinger Jun 25 '19 at 14:19

4 Answers4

9

There is at least one real world use case for clearerr: when you want to mimic tail -f on a file that is not opened in exclusive mode. That means that another (or many other) process(es) write at the end of a file, and one process repeatedly reads even after having reached the end of file in order to look whether new data has arrived. In that case, could would look like:

for (;;) {
    if (NULL == fgets(line, sizeof(line), fd)) {
        sleep(n);
        clearerr(fd);     // reset EOF condition
    }
    else {
        fputs(line, fdout);
    }
}
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Does this require an feof() test to distinuish EOF case from ferror() case? If the NULL return is from an error and not from EOF I would not know what to do. – Kelvin Jun 25 '19 at 14:24
  • @TobySpeight: Oops I had forgotten the `-f`. Thanks for noticing! Post edited – Serge Ballesta Jun 25 '19 at 15:14
3

Functions that set the error status of a FILE (as reported by ferror) do not clear it even if later called successfully. Likewise if you encounter the end of file while reading, it will not be cleared automatically even if the file later has more data available.

Basically this means that if you are using ferror to check for an error state and you have some way of recovering from it, the ferror will keep indicating an error until you use clearerr.

In your example, if you just use the return value of fread as the condition for terminating the read (i.e., EOF and any type of error are considered final), there is no need to clearerr: just fall through to fclose (and perhaps use ferror to determine whether to print an error message).

On the other hand, if the FILE is in fact a stream on which read can later succeed, and you detect (or assume) that specific condition and retry, you should clearerr before retrying or you will keep seeing the old error condition on future attempts.

Likewise, as pointed out in comments, clearerr also clears the end of file state, so this also applies when using feof to check for the end of file. (Note, however, that you generally shouldn't use !feof(file) as the loop condition when reading.)

Arkku
  • 41,011
  • 10
  • 62
  • 84
  • 3
    Important clarification: `clearerr()` clears the error flag ***and*** the end-of-file flag. The latter part is rather more likely to be useful than the former. – John Bollinger Jun 25 '19 at 14:11
2

clearerr() clears the error and EOF flags from a stream.

Say FILE were like this:

typedef struct {
    int fd;
    char *buf;
    int error;
    int eof;
} FILE;
FILE *file;

This would set file->error and file->eof to 0.

Some reasons for doing this include file I/O, such as when a file gives EOF, but then another program (or another thread, etc.) appends to it. If you clear the error after doing this, you can have your program act as sort of a tail -f-substitute.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
2

clearerr() clears both the error and end-of-file flags.

A pedantic use of 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;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256