The other day I was working on a code where in I was doing some file related operations. I used the function feof() to test whether the end of file has been reached or not. I got into some trouble over the way I used this function in my code, after debugging a bit I found the problem. The problem was very much related to the understanding of the feof() API. So, I thought to share it with all the members and visitors of G4E. The code Lets first look at the code snippet : Code: #include<stdio.h> int main(void) { FILE *fd = NULL; char buff[20]; fd = fopen("data.txt","r"); if(fd) { while(!feof(fd)) { fgets(buff, sizeof(buff), fd); puts(buff); } fclose(fd); } return 0; } The code above : Tries to open a text file data.txt in the read only mode. If the file is successfully opened for reading then a while loop is started. The loop runs until all the lines in the text file are read and displayed. Finally, the file is closed. So all in all the expectation from the above code is to read the text file line by line and display each line read. Lets first see the text file : Code: abc def ghi Lets see the output I was getting : Code: ~/practice $ ./feof abc def ghi ghi If you analyze the output above, you will see that the line 'ghi' is displayed twice. Well, this was the cause of concern to me that why the last line is being displayed twice. I kept on debugging the problem for some time and very soon I zeroed down to the function feof(). Then I started studying the the function feof(). The Debugging As a first step I visited the man page of function feof() but he man page did not have anything to contribute to the solution of this problem. Only the following definition was present there : In the second step, I thought of using the errno variable to see if some error occurred or not. So I changed the code to something like : Code: #include<stdio.h> #include<errno.h> #include<string.h> int main(void) { FILE *fd = NULL; char buff[20]; fd = fopen("data.txt","r"); if(fd) { errno = 0; while(!feof(fd)) { printf("\n [%s] \n", strerror(errno)); fgets(buff, sizeof(buff), fd); puts(buff); errno = 0; } fclose(fd); } return 0; } Code: #include<stdio.h> #include<errno.h> #include<string.h> int main(void) { FILE *fd = NULL; char buff[20]; fd = fopen("data.txt","r"); if(fd) { errno = 0; while(!feof(fd)) { printf("\n [%s] \n", strerror(errno)); fgets(buff, sizeof(buff), fd); puts(buff); errno = 0; } fclose(fd); } return 0; } In the above code I introduced the usage of a global variable 'errno' which contains the error code in case some error occurred in this API. The function strerror() takes the variable 'errno' as argument and returns a string of error description. If no error occurred, the string returned by this API is 'Success'. The output of the above code was : Code: ~/practice $ ./feof [Success] abc [Success] def [Success] ghi [Success] ghi This concluded that the API feof() never failed during the execution. After the second step(mentioned above), I was again in a fix. Then suddenly I thought to trace the forward movement of file descriptor in the file. So, now firstly I found the exact number of bytes in the text file. It came out to be 12 bytes(see the command below). Code: ~/practice $ ls -lart data.txt -rw-r--r-- 1 himanshu family 12 2011-11-16 21:25 data.txt Now I changed the code to something like : Code: #include<stdio.h> int main(void) { FILE *fd = NULL; char buff[20]; fd = fopen("data.txt","r"); if(fd) { while(!feof(fd)) { fgets(buff, sizeof(buff), fd); printf("\n The file position indicator is at : [%lu]\n",ftell(fd)); puts(buff); } fclose(fd); } return 0; } This time I introduced a call to ftell() that will let the print() display the file position indicator value. The output of the above code was : Code: ~/practice $ ./feof The file position indicator is at : [4] abc The file position indicator is at : [8] def The file position indicator is at : [12] ghi The file position indicator is at : [12] ghi The last two lines had my eyes stuck on them. This gave me an idea that despite of reaching the EOF, something is happening due to which feof() is not detecting that EOF is reached and it takes one more iteration for feof() to detect it. Then In the final step I researched a bit on INTERNET and found that feof() function sets the end of file indicator when the EOF character is actually read. This means when 'ghi' is read for the first time, then EOF is not read along with it. So, no end-of-file indicator is set and feof() returns zero and hence the flow enters the while loop again and then fgets comes to know that the next character is EOF and hence it discards it and returns NULL but sets end-of-file indicator but since the buffer 'buff' that is passed to fgets() was not flushed and hence it still contains the last read in value 'ghi' so it seemed that fgets() returns 'ghi' again. Now feof() detects end-of-file indicator is set and it returns a non zero value and so the while loop breaks. Ohh...I said and that cleared all my doubts The Solution When you know a problem in and out then it rarely happens in software development that you can't find the solution. Same was the situation here, since now the whole problem was clear so it did not take much time to solve it. Here is the code : Code: #include<stdio.h> int main(void) { FILE *fd = NULL; char buff[20]; fd = fopen("data.txt","r"); if(fd) { while(fgets(buff, sizeof(buff), fd)) { puts(buff); } fclose(fd); } return 0; } In the above code, I completely got away with the function feof() since I came to know that fgets() returns NULL when EOF is read. Here is the output : Code: ~/practice $ ./feof abc def ghi The output this time was expected. Conclusion To conclude, this article gives a good overview of how feof() works internally and how we can use it. Stay tuned for more!!!
You have really increased the quality of C API articles on G4E. Thanks a lot.. But i think u should have used feof , in the final corrected code.
Thanks for the appreciation buddy. That was exactly the way I went about with my solution, so I explained it the same way in this article. Now it would be good that If you come up with a solution using feof(). ;-)
simple & easiest way is to check for eof imdtly after read. eg: fopen(...); fread/fgets(......); while (!eof) { printf(.....); fread/fgets(....); } fclose(...);