Understand feof() function In C

Discussion in 'C' started by poornaMoksha, Nov 16, 2011.

  1. poornaMoksha

    poornaMoksha New Member

    Joined:
    Jan 29, 2011
    Messages:
    150
    Likes Received:
    33
    Trophy Points:
    0
    Occupation:
    Software developer
    Location:
    India
    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!!!
     
    Scripting and lionaneesh like this.
  2. lionaneesh

    lionaneesh Active Member

    Joined:
    Mar 21, 2010
    Messages:
    848
    Likes Received:
    224
    Trophy Points:
    43
    Occupation:
    Student
    Location:
    India
    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.
     
    shabbir likes this.
  3. poornaMoksha

    poornaMoksha New Member

    Joined:
    Jan 29, 2011
    Messages:
    150
    Likes Received:
    33
    Trophy Points:
    0
    Occupation:
    Software developer
    Location:
    India
    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(). ;-)
     
  4. oldisgold

    oldisgold New Member

    Joined:
    Apr 16, 2012
    Messages:
    4
    Likes Received:
    0
    Trophy Points:
    0
    simple & easiest way is to check for eof imdtly after read. eg:

    fopen(...);
    fread/fgets(......);
    while (!eof)
    { printf(.....);
    fread/fgets(....);
    }
    fclose(...);
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice