Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
581 views
in Technique[技术] by (71.8m points)

c - How a stream error indicator affects following input code?

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

Speaking specifically to the provisions of the standard, I'm inclined to agree with your analysis:

  • Few functions are specified to clear the error indicator of a stream, and fgetc() is not one of them. More generally, none of them are data-transfer functions. Therefore, if the error indicator is set for a stream before that stream is presented to fgetc() for reading, then it should still be set when that function returns, all other considerations notwithstanding. That covers these cases:*

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

    It also covers this case with respect to the expected value of the error indicator, though it does not speak to whether it can actually happen:

    1  0   1   Normal reading of valid data with error indicator set!
    
  • fgetc() is specified to return EOF in every situation in which it is specified to set the end-of-file indicator on a stream. Therefore, if fgetc() returns anything other than EOF then it will not, on that call, have set the stream's error (or end-of-file) indicator. That covers these cases:

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

    On the other hand, if fgetc() does return EOF then either the stream's end-of-file indicator or its error indicator should afterward be found set. But the standard distinguishes between these cases, and specifies that the user can distinguish them via the feof() and ferror() functions. That covers these cases:*

    0  1   0   End-of-file
    0  1   1   Input error
    
  • Finally, I concur that none of the behavior of fgetc() is conditioned on the initial state of the stream's error indicator. Provided only that the stream is not initially positioned at its end, and its end-of-file indicator is not initially set, "the fgetc function returns the next character from the input stream pointed to by stream." That establishes that this, the case of most interest, is in fact allowed:

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

    However, that the case is allowed in the abstract does not imply that it can be observed in practice. The details seem unspecified, and I would expect them to depend on the implementation of the driver serving the stream in question. It is entirely possible that having once encountered an error, the driver will continue to report an error on subsequent reads until reset appropriately, and perhaps longer. From the C perspective, that would be interpreted as an (additional) error occurring on each subsequent read, and nothing in the language specifications prevents that. Not even use of one of the functions that clear a stream's error indicator.

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?

I agree that if a stream's error indicator is initially set, its end-of-file indicator is not, and reading it with fgetc() returns EOF, then ferror() does not usefully distinguish between the end-of-file and error cases whereas feof() should.

On the other hand, whether one can usefully continue to read a given stream after an error has been encountered on it depends on implementation and possibly on specific circumstances. That applies even if the error indicator is cleared via a clearerr() call, not to mention if the error indicator is not cleared.


* Although I agree that there is an ambiguity with respect to EOF in the event that UCHAR_MAX > INT_MAX, I assert that that is just one of several reasons why such an implementation would be problematic. As a practical matter, therefore, I disregard such implementations as entirely hypothetical.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...