Search code examples
cfilebinaryfileseofstdio

There is always a "Nothing" byte at the end of file


For example this data file abc.txt

abc

Note that there is no newline at the bottom.

When I write the following program in C

#include <stdio.h>

int main(){
    FILE *fp = fopen("abc.txt","rb");  // NOTE this is "rb"

    while (!feof(fp)){
        fprintf(stdout, "%c", fgetc(fp));
        fprintf(stdout, "%d", feof(fp));
    }

    fclose(fp);

    return 0;
}

The stdout result is like this:

[xxx@xxx hello]$ ./a.out 
a0b0c0
0�1[xxx@xxx hello]$ 

What is the additional output bytes in the last line?


Solution

  • feof reports whether the EOF indicator has been set, and it's set when you try to read past the end of the file. So the final fgetc reads past the end of the file, returns EOF (typically -1), and then feof returns 1.

    This is made more clear if you use %d rather than %c to show the result of fgetc:

    #include <stdio.h>
    
    int main(){
        FILE *fp = fopen("abc.txt","rb");  // NOTE this is "rb"
    
        while (!feof(fp)){
            fprintf(stdout, "%d:", fgetc(fp));
            fprintf(stdout, "%d\n", feof(fp));
        }
    
        fclose(fp);
    
        return 0;
    }
    

    Output:

    97:0
    98:0
    99:0
    -1:1
    

    I'd probably write the code avoiding feof, and use that fgetc returns EOF when there's an error or the end of the file has been reached.

    #include <stdio.h>
    
    int main(){
        FILE *fp = fopen("abc.txt","rb");  // NOTE this is "rb"
    
        while (1) {
            int c = fgetc(fp);
            if (c == EOF) break;
            printf("%c", c);
        }
    
        fclose(fp);
    
        return 0;
    }
    

    Note that this code is still flawed because it treats errors like EOF: you should check if there was a read error using ferror.