cfgetc

What are all the reasons `fgetc()` might return `EOF`?


Certainly fgetc() returns EOF when end-of-file or an input error occurs.
Is that all and does that mean no more data is available?

FILE *inf = ...;
int ch;
while ((ch = fgetc(inf)) != EOF) {
  ;
}
if (feof(inf)) puts("End-of-file");
else if (ferror(inf)) puts("Error");
else puts("???");

Is testing with feof(), ferror() sufficient?

Note: EOF here is a macro that evaluates to some negative int, often -1. It is not a synonym for end-of-file.

I have found some questions and more that are close to this issue, yet none that enumerate all possibilities.


Solution

  • Is that all and does that mean no more data available?

    No, there are more ways for EOF.
    An EOF does not certainly mean no more data - it depends.

    The C library lists three cases where fgetc() returns EOF.

    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. C17dr § 7.21.7.1 3

    Recall each stream, like stdin, has an end-of-file indicator and error indicator.

    Other cases


    A robust way to handle the return from fgetc().

    FILE *inf = ...;
    if (inf) {  // Add test
      int ch; // USE int !
    
      // Pedantic considerations, usually can be ignored
      #if UCHAR_MAX > INT_MAX
        clearerr(inf); // Clear history of prior flags
        while ((ch = fgetc(inf)) != EOF && !feof(inf) && !ferror(inf)) {
          ;
        }
      #else
        while ((ch = fgetc(inf)) != EOF) {
          ;
        }
      #endif
    
      if (feof(inf)) puts("End-of-file");
      else puts("Error");
    

    If code needs to look for data after end-of-file or error, call clearerr() and repeat the if() block.