Each stream has "an error indicator that records whether a read/write error has occurred".
It is set, usually rarely, by various functions: fgetc(), fflush(), fseek(), ...
.
It is cleared by various functions: rewind(), clearerr(), fopen(), ...
.
int ferror(FILE *stream)
reports the state.
The ferror
function returns nonzero if and only if the error indicator is set for stream
.
In this case, certainly an input error just occurred.
if (!ferror(istream)) {
int ch = fgetc(istream);
if (ch == EOF && ferror(istream)) {
puts("Input error just occurred");
}
}
Exploring fgetc()
deeper, fgetc()
does not return EOF
because the error indicator was set, but because "If a read error occurs" or end-of-file related reasons1. Usually once an error occurs (e. g. parity error on a serial stream), code does not continue reading without clearing the error, yet consider what happens when it does continue.
I see 8 situations: the error indicator set/clear prior to fgetc()
, fgetc()
returns EOF
or not, and a following ferror()
could be true or not.
int e1 = !!ferror(istream);
int eof = fgetc(istream) == EOF;
int e2 = !!ferror(istream);
Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones? especially is valid input possible with error indicator set? 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
With the error indicator set prior to an input operation, things become complicated and clearing it beforehand simplifies code. Yet that prevents error indicator accumulation.
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?
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 == '
') break;
buf[i++] = ch;
}
buf[i] = 0;
Similar questions
File operations with error indicator set
. This unanswered one centers on accumulating error indicator without testing fgetc()
return value (answers venture off into errno
and making a user error flag) and this one is trying to simply disambiguate a fgetc()
.
fgetc(): Is it enough to just check EOF? does not address error indicator set prior to fgetc()
.
Similar issues apply to output and I/O streams, yet this question focuses on input streams.
1 int fgetc(FILE *stream)
Returns
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 On cases 0-1-0, 1-1-1. Seems if UCHAR_MAX == UINT_MAX
, a unsigned char
, could be returned and equate to EOF
and not be due to end-of-file nor input error.
See Question&Answers more detail:
os