Thread: feof, fgets, determining if EOF is forthcoming

  1. #1
    Gates' arch nemesis
    Join Date
    Oct 2004
    Posts
    18

    feof, fgets, determining if EOF is forthcoming

    This is probably old ground to experienced users, but I've never read a solution or workaround. I have a construct:
    Code:
    while(fgets...) {
        if (feof
    }
    The problem is that feof remains false if the input file ends with 0D 0A (CRLF), so I get no indication that I'm on the last record. I need to know "in advance" that there are no records following. Now without the ending CRLF, feof "signals" that the next fgets will fail, and I know I'm on the last record. You can see this for yourself with an input file consisting of 1 total byte as the letter A, and then doing it with a CRLF for a file size of 3. feof reports that "the end is near!" only on the 1 byte case.

    This is all just to illustrate what I know is by design; fgets() doesn't see a newline character in the 1-byte-file case, so indeed you have hit EOF once it fires. In the 3-byte-file case, fgets got his newline character and stopped then. The program doesn't know if there's more following, because fgets stopped when he got his newline, and didn't need to look any further; even though he's literally on the doorstep of EOF, an according-to-Hoyle EOF has not occurred.

    So here is what this is leading to. I want to know, within the fgets loop, if EOF is imminent. I can't rely on feof as described before; it gives a different result depending if the file ends with newline. Obviously it would be atrocious to "peek" with an extra fgets and rewind. So what is the civilized best coding practice solution? All I've come up with is
    Code:
    while(fgets...) {
        if (icount>0) {
    	/* process the PRIOR pass of fgets which I've buffered away */
        }
        /* Now buffer away the the CURRENT pass of fgets */    
    }
    /* Now process the final pass of fgets after the loop fails.
       I will give special treatment to the final record, so different code here.
     */
    But that's so unclean!

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    If you want to process each line of input without saving all of them, then I think your approach is sound. Not elegant, but sound.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Do you have an example of such a file you can attach?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Gates' arch nemesis
    Join Date
    Oct 2004
    Posts
    18
    Quote Originally Posted by Salem View Post
    Do you have an example of such a file you can attach?
    Sorry my post was so long, but that's covered. Open the editor Notepad, type the letter A, and save . Run code. Open the saved file. Go right arrow, hit enter key, save, and run code again

    As to code you need stdio, a FILE var., a char buffer for fgets, and fopen/fclose.

  5. #5
    Registered User
    Join Date
    Sep 2012
    Posts
    357
    fgets() return NULL on two occasions: on error or on end-of-file.
    If you do the usual

    Code:
    while (fgets(/* ... */)) { /* ... */}
    you will only exit the while loop when either you recahed end-of-file or there was an error reading the file.
    If you must identify the reason for the loop terminating (because you can reopen the file (from another directory) in case of error), then that's a good time time to use feof() (or ferror()); otherwise you probably shouldn't use them.

    If you need to check whether fgets() read an empty line, check for that inside the loop

    Code:
    while (fgets(buffer/* ... */)) {
        if (*buffer == '\n') /* empty line */; /* assumes text-mode and proper line break translation */
    }

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Unless you're passing a very short buffer to fgets, then each buffer will have a \n at the end of it.

    Code:
    while ( fgets(buff,sizeof(buff),fp) != NULL ) {
        if ( isValidBuffer(buff) ) {
            // do stuff
        }
    }
    Now if you decide you don't like buffers without a \n at the end, or consisting only of a \n, then you can make your validation function flag those up.

    Oh, and your instructions are meaningless - I don't have windows or notepad.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Gates' arch nemesis
    Join Date
    Oct 2004
    Posts
    18
    Salem, that's a good idea, but it still doesn't signal EOF. If there is a terminating '\n' (and 0), then it's valid, and I don't see that EOF if about to follow. If there is no '\n', then I could just use feof as I initially discussed.

    Unfortunately there is no guarantee that the last record of input has an ending linefeed. Yes, "probably" there will be, but...

    Thanks for your analysis.

  8. #8
    Registered User
    Join Date
    Sep 2012
    Posts
    357
    Quote Originally Posted by GatesAntichrist View Post
    ... still doesn't signal EOF ...
    There's no way to check for EOF before triggering the end-of-file condition.
    C doesn't have a function to ascertain if the next read fails because there is no more data. The way to check for EOF is to assume there is no EOF, read the data, and check for the condition aferwards.

    As you say, you can (but prolly shouldn't) read each character twice, checking for EOF at the first read, then ungetc() the character, and read it again. Mind that you can only portably unget 1 single character at a time, so no scanf(), no fgets() or any other input function that consumes more than a single character can be (portably) used.

    Code:
    int nextreadtriggersEOForERROR(FILE *handle) {
        int ch = fgetc(handle);
        if (ch == EOF) {
            if (feof(handle)) return 1; /* EOF */
            return 2;                   /* ERROR */
        }
        ungetc(ch, handle);
        return 0;
    }

  9. #9
    Gates' arch nemesis
    Join Date
    Oct 2004
    Posts
    18
    > There's no way to check for EOF before triggering the end-of-file condition.

    Argh! Well that's a succinct sober statement. It needed to be said, so good! I've been grasping for a ghost it seems.

    Doh! An idea just hit me; I could compare to filesize
    Code:
    fseek(fp, 0L, SEEK_END);
    sz = ftell(fp)
    That seems a hell of a lot less barbaric then the buffering approach I contemplated in the first post.

    By golly, I'm going to call this solved, though smart additional ideas are welcome.

  10. #10
    Gates' arch nemesis
    Join Date
    Oct 2004
    Posts
    18
    This looks reasonable, grabbed off the net
    Code:
    #include <stdio.h>
    long GetFileSize(FILE *fp);
    int
    main(void)
    {
       FILE *fpin;
    
       fpin = fopen("myfile.dat", "r");
       printf( "File size of myfile.dat is %ld bytes\n", GetFileSize(fpin) );
       fclose(fpin);
       return 0;
    }
    long GetFileSize(FILE *fp)
    {
       long curpos, length;
    
       curpos = ftell(fp);               /* Save current position in the file */
       fseek(fp, 0L, SEEK_END);      /* Set file pointer at end of file */
       length = ftell(fp);              /* Get current position --> file size */
       fseek(fp, curpos, SEEK_SET); /* Restore source file position */
       return length;
    }

  11. #11
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    948
    Quote Originally Posted by GatesAntichrist View Post
    This looks reasonable, grabbed off the net
    Code:
    #include <stdio.h>
    long GetFileSize(FILE *fp);
    int
    main(void)
    {
       FILE *fpin;
    
       fpin = fopen("myfile.dat", "r");
       printf( "File size of myfile.dat is %ld bytes\n", GetFileSize(fpin) );
       fclose(fpin);
       return 0;
    }
    long GetFileSize(FILE *fp)
    {
       long curpos, length;
    
       curpos = ftell(fp);               /* Save current position in the file */
       fseek(fp, 0L, SEEK_END);      /* Set file pointer at end of file */
       length = ftell(fp);              /* Get current position --> file size */
       fseek(fp, curpos, SEEK_SET); /* Restore source file position */
       return length;
    }
    That might not work as expected on text streams:
    For text streams, the numerical value may not be meaningful but can still be used to restore the position to the same position later using fseek
    (from ftell - C++ Reference)
    On at least one implementation that I've seen, ftell returns a small value like 0, 1, 2, etc each time it is called. Presumably the C library stores actual file positions in an array and the return value from ftell is used as an index into that array. Of course, that array is implementation-specific and cannot be accessed by the user program. Other implementations might return the actual file position. Either way, you can't depend on the return value to determine the file size (you could open the file in binary mode, but then fgets and other input functions won't translate system-specific end-of-line sequences into '\n').

  12. #12
    Gates' arch nemesis
    Join Date
    Oct 2004
    Posts
    18
    Quote Originally Posted by christop View Post
    That might not work as expected
    <snip>
    On at least one implementation that I've seen, ftell
    <snip>
    implementation-specific
    <snip>
    [So, instead,] open the file in binary mode
    Fine analysis. Very clear.

  13. #13
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Still, without an actual attachment file we can look at, I still think you're digging a hole for yourself where none needs to be dug.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. while(!feof)
    By mconflict in forum C Programming
    Replies: 12
    Last Post: 04-02-2012, 12:30 PM
  2. Fgets,finding end of file without using eof feof
    By rac1 in forum C Programming
    Replies: 12
    Last Post: 12-26-2011, 04:53 PM
  3. HELP: feof
    By dlf723 in forum C Programming
    Replies: 5
    Last Post: 07-23-2010, 08:49 AM
  4. feof() from FAQ
    By salvadoravi in forum C Programming
    Replies: 6
    Last Post: 01-25-2008, 01:08 PM
  5. cannot get out of while( !feof (f) )
    By SoFarAway in forum C Programming
    Replies: 2
    Last Post: 02-19-2005, 03:36 PM