Don't Use feof() To Control A File Read Loop
Updated:
How do you successfully create a file reading loop by checking if you have reached the end of the file?
In C the function feof checks for the end-of-file indicator associated with the file stream.
int feof (FILE *stream);
When processing a file line by line you may be tempted to create a loop controlled directly by this indicator, writing something like this:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp;
char buf[100];
int line_counter = 0;
fp = fopen("test_file.txt", "r");
if (!fp) {
return EXIT_FAILURE;
}
while (!feof(fp)) {
fgets(buf, sizeof(buf), fp);
printf("%d -> %s", ++line_counter, buf);
}
fclose(fp);
return EXIT_SUCCESS;
}
In this code the problem is that we are not verifying that the read operation was successful. We are just assuming it. So the first aspect to underline is that before processing the data (here we have just a printf) we must verify that we have really read something. But that’s not all.
The end-of-file indicator does not set itself
Assuming that the file stream still has data to read just by checking feof actually makes no sense. In fact, the end-of-file indicator is set only after a read attempt has brought the file position indicator to the end of the file.
If we take a step back and look at the picture, we can imagine that it is unreasonable to ask an I/O system if it will be able to do a read operation in the future. The only way to know is to try reading.
Now we understand that the correct way is to check the return value of the read function. In our example we use fgets. If this function ends successfully, it returns the pointer to the buffer where the data just read has been stored, otherwise it returns a NULL pointer. This function will then set the eof indicator if an end of file condition occurs. However, a reading attempt can fail for other reasons as well.
So, here is the correct code to make a file read loop, reading one line at a time.
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp;
char buf[100];
int line_counter = 0;
fp = fopen("tt", "r");
if (!fp) {
return EXIT_FAILURE;
}
while (fgets(buf, sizeof(buf), fp) != NULL) {
printf("%d -> %s", ++line_counter, buf);
}
fclose(fp);
return EXIT_SUCCESS;
}
Is feof useless?
You may have noticed that we no longer check the end-of-file condition in the final version of our example. We rarely really care to know it. What matters is to know if the I/O operation was successful or not and act accordingly, rather than knowing why it failed.