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 );
}
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);
}
}
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.)
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.
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With