Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to check for EOF after fgets

1,057 views
Skip to first unread message

Lou Zher

unread,
Apr 8, 2000, 3:00:00 AM4/8/00
to
Robert Jon Bredlau <rbre...@cats.ucsc.edu> wrote in message
news:8cm0qu$9...@darkstar.ucsc.edu...
[snip]
> My question is, what does it return if characters are read from a file and
>EOF is encountered during the read? NULL is only returned on a read error
or
>if no characters are read, otherwise it returns the pointer. This leads me
>to believe it would read the characters until EOF and stop and on the next
>call would encounter EOF and return NULL. However, I need to distinguish
>between these two cases because if a read error occurs, I need to free a
>buffer I've created but I don't need to free it if it has read in
characters
>followed by EOF. I'm trying the function feof after each call but it isn't
>helping any. Here's the function I'm I'm trying to write, I don't have to
say
>it because I know you will but feel free to criticize my code.
[snip]

Check out feof and ferror.

-LZ

Robert Jon Bredlau

unread,
Apr 8, 2000, 3:00:00 AM4/8/00
to
First an excerpt from the man page for fgets on this machine:
fgets() reads characters from the stream into the array
pointed to by s, until n-1 characters are read, a NEWLINE
character is read and transferred to s, or an EOF condition
is encountered. The string is then terminated with a null
character. fgets() returns its first argument.
DIAGNOSTICS
If EOF is encountered and no characters have been read, no
characters are transferred to s and a NULL pointer is
returned. If a read error occurs, such as trying to use
these functions on a file that has not been opened for read-
ing, a NULL pointer is returned. Otherwise s is returned.

My question is, what does it return if characters are read from a file and
EOF is encountered during the read? NULL is only returned on a read error or
if no characters are read, otherwise it returns the pointer. This leads me
to believe it would read the characters until EOF and stop and on the next
call would encounter EOF and return NULL. However, I need to distinguish
between these two cases because if a read error occurs, I need to free a
buffer I've created but I don't need to free it if it has read in characters
followed by EOF. I'm trying the function feof after each call but it isn't
helping any. Here's the function I'm I'm trying to write, I don't have to say
it because I know you will but feel free to criticize my code.

char *Read_Line_Cmn( FILE *fpFile )
{
char *line = NULL, *temp = NULL;
int buff_size = 0, index = 0;

/* Calc new buffer size and malloc */
buff_size += MALLOC_STEP;
line = malloc( buff_size );
if( line == NULL ){ return NULL; }
/* Loop and read the line until EOF or '\n' is encountered. */
/* Reallocate memory as necessary to hold entire line. */
while( TRUE )
{
temp = line;
line = fgets( &line[index], MALLOC_STEP, fpFile );
if( feof( fpFile )){ printf("common.c: EOF\n");line = temp; break; }
if( line == NULL ){ free( holder ); return NULL; }
line = temp;
if( (temp = strchr( line, '\n' ))){ temp[0] = 0; break; }
buff_size += MALLOC_STEP;
line = realloc( line, buff_size );
index = strlen( line );
}
return line;
}

Thanks in advance,

Robert

Lou Zher

unread,
Apr 8, 2000, 3:00:00 AM4/8/00
to
Robert Jon Bredlau <rbre...@cats.ucsc.edu> wrote in message
news:8cm0qu$9...@darkstar.ucsc.edu...
> My question is, what does it return if characters are read from a file and
>EOF is encountered during the read? NULL is only returned on a read error
or
>if no characters are read, otherwise it returns the pointer. This leads me
>to believe it would read the characters until EOF and stop and on the next
>call would encounter EOF and return NULL.

Whoops, I didn't quite read the whole thing. Okay fread will return to you
an integer count of characters upto the EOF. At this point, C would set it's
internal eof flag. The next time you call it, you'll get a zero back because
of the eof. If you check feof before every fread, you should never get back
a zero from fread unless there is a real live read error. If you don't want
to check feof before every read, then once you get an zero back, check feof.
If a read error occured before the eof, that flag won't be set. Or you could
check ferror.

-LZ

Robert Jon Bredlau

unread,
Apr 8, 2000, 3:00:00 AM4/8/00
to
What I gather from your post is that I should use fread but I'm not sure if
this would be helpful. My goal is to read in a line from any file of arbitrary
length. I'm doing this by mallocing memory and starting a loop. In the loop,
I use fgets to read into the memory. Then I check for a newline (That would
signal the end of the line) and fgets stops reading on a '\n'. If there is
no '\n', I reallocate more memory with realloc and continue reading the line
where I left off the last time. I just need to know when to exit the loop.
According to the man page for fgets, fgets returns NULL if no characters
are read and EOF is encountered or during a read error. Since I've malloced
my buffer, if a read error occurs I need to free it. But if characters are
read and then EOF is encountered, I need to return the buffer to the caller.
I've tried feof but it's not working like I think it should. As far as I can
tell, my code is looping and reading the line fine and on the second to the
last read its reading up to the EOF and stopping. Then my code is reallocating
more memory and doing the read again. Since all that's left is the EOF, fgets
is returning NULL and my loop is ending but freeing the buffer because I can't
determine if this was a read error or not.
Now you're suggesting that I use fread but I'm worried I'll run into another
problem if I do. fgets stops reading on a '\n' where fread reads nitems of
nsize. I'd have no way of seeing the '\n' until I read past it and started on
the next line or unless I read one character at a time.
Any ideas out there?

Robert

Lou Zher (ab...@localhost.com) wrote:
: Robert Jon Bredlau <rbre...@cats.ucsc.edu> wrote in message

:
:

Lou Zher

unread,
Apr 8, 2000, 3:00:00 AM4/8/00
to
Robert Jon Bredlau <rbre...@cats.ucsc.edu> wrote in message
news:8cm4cn$a...@darkstar.ucsc.edu...

Man, I suck. I mis-read that whole message like twelve different ways. Well,
the same thing applies for fgets. If, in the middle of a line, you have a
^Z, fgets puts the good stuff in your buf and says all's good, but also sets
the eof flag. If you check feof at this point, it will return true. fgets
will fail if you call it again.

char buffer[256], *ptr=buffer;
while (!feof(stream))
{
ptr = fgets(buffer, sizeof(buffer), stream)
if (ptr==NULL)
{
printf("Read error");
break;
}
puts(buffer); // display line
}
if (ptr!=NULL)
printf("EOF");

another call to fgets here will return a NULL.

if your file looks like this:
line 1 blah, blah, blah
line 2 blah, bl^z

the first fgets gives you "line 1 blah, blah, blah"
the second fgets gives you "line 2 blah, bl"
a check of feof, returns true at this point
the third fgets gives you NULL.

Wow... sorry about the confusion before.

-LZ

Floyd Davidson

unread,
Apr 8, 2000, 3:00:00 AM4/8/00
to
rbre...@cats.ucsc.edu (Robert Jon Bredlau) wrote:
>
>char *Read_Line_Cmn( FILE *fpFile )
>{
> char *line = NULL, *temp = NULL;
> int buff_size = 0, index = 0;
>
> /* Calc new buffer size and malloc */
> buff_size += MALLOC_STEP;
> line = malloc( buff_size );
> if( line == NULL ){ return NULL; }
> /* Loop and read the line until EOF or '\n' is encountered. */
> /* Reallocate memory as necessary to hold entire line. */
> while( TRUE )
> {
> temp = line;
> line = fgets( &line[index], MALLOC_STEP, fpFile );
> if( feof( fpFile )){ printf("common.c: EOF\n");line = temp; break; }
> if( line == NULL ){ free( holder ); return NULL; }
> line = temp;
> if( (temp = strchr( line, '\n' ))){ temp[0] = 0; break; }
> buff_size += MALLOC_STEP;
> line = realloc( line, buff_size );
> index = strlen( line );
> }
> return line;
>}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STEP 2

char *Read_Line_Cmn(FILE *fpFile)
{
char *temp, *line = NULL;
int size = 1;

while (1) {
size += STEP - 1;;

if (!(temp = realloc(line, size))) {
/* error, out of memory */
if (line) {
free(line); /* free if memory was allocated */
}
line = NULL; /* return value on error */
break;
}

line = temp;

if (!(fgets(line + size - STEP, STEP, fpFile))) {
/* no input read to buffer */
if (feof(fpFile)) {
/* end of file encountered */
break;
}
if (ferror(fpFile)) {
/* error encountered, discard current input */
free(line); /* free allocated memory */
line = NULL; /* return value on error */
break;
}
}

if ((temp = strchr(line, '\n'))) {
/* end of line encountered */
*temp = '\0'; /* delete newline */
break;
}
}

/* returns NULL on error, or ptr to string in a malloc'ed buffer */
return line;
}


--
Floyd L. Davidson fl...@barrow.com
Ukpeagvik (Barrow, Alaska)

Robert Jon Bredlau

unread,
Apr 8, 2000, 3:00:00 AM4/8/00
to
Well, I finally got it to work. Here's the finished function if anyone wants
to use it. I'm not sure how fool proof it is but it did read in a line with
over 40,000 characters.

#define MALLOC_STEP 10

/******************************************************************************
** Function : Read_Line_Cmn
** Usage : charptr = Read_Line_Cmn( file );
** file : File stream to read line from
** Read a line of arbitrary length from file not including the '\n'
** and return the pointer to the client. Note that the client
** should free( charptr ) on this pointer. NULL is returned on any
** error, otherwise a pointer to the newly allocated buffer
** containing the line. Note the client should free this buffer.
******************************************************************************/


char *Read_Line_Cmn( FILE *fpFile )
{
char *line = NULL, *temp = NULL;
int buff_size = 0, index = 0;

/* Calc new buffer size and malloc */
buff_size += MALLOC_STEP;
line = malloc( buff_size );
if( line == NULL ){ return NULL; }
/* Loop and read the line until EOF or '\n' is encountered. */
/* Reallocate memory as necessary to hold entire line. */

while( !feof( fpFile ))


{
temp = line;
line = fgets( &line[index], MALLOC_STEP, fpFile );

if( line == NULL ){ free( line ); line = NULL; break; }


line = temp;
if( (temp = strchr( line, '\n' ))){ temp[0] = 0; break; }
buff_size += MALLOC_STEP;

if( !(temp = realloc( line, buff_size )))
{ free( line ); line = NULL; break; }
line = temp;


index = strlen( line );
}
return line;
}

Once again, thanks for the help folks. 8)

Robert

Floyd Davidson (fl...@ptialaska.net) wrote:

:
I stole this chunk of code. Thanks!
: if (!(temp = realloc(line, size))) {

Floyd Davidson

unread,
Apr 8, 2000, 3:00:00 AM4/8/00
to
rbre...@cats.ucsc.edu (Robert Jon Bredlau) wrote:
>Well, I finally got it to work. Here's the finished function if anyone wants
>to use it. I'm not sure how fool proof it is but it did read in a line with
>over 40,000 characters.

You're closer, but not quite there yet.

>#define MALLOC_STEP 10
>
>/******************************************************************************
>** Function : Read_Line_Cmn
>** Usage : charptr = Read_Line_Cmn( file );
>** file : File stream to read line from
>** Read a line of arbitrary length from file not including the '\n'
>** and return the pointer to the client. Note that the client
>** should free( charptr ) on this pointer. NULL is returned on any
>** error, otherwise a pointer to the newly allocated buffer
>** containing the line. Note the client should free this buffer.
>******************************************************************************/
>char *Read_Line_Cmn( FILE *fpFile )
>{
> char *line = NULL, *temp = NULL;
> int buff_size = 0, index = 0;
>
> /* Calc new buffer size and malloc */
> buff_size += MALLOC_STEP;
> line = malloc( buff_size );
> if( line == NULL ){ return NULL; }
> /* Loop and read the line until EOF or '\n' is encountered. */
> /* Reallocate memory as necessary to hold entire line. */
> while( !feof( fpFile ))

This can just as well be while (1), because feof() will never
return non-zero.

> {
> temp = line;
> line = fgets( &line[index], MALLOC_STEP, fpFile );
> if( line == NULL ){ free( line ); line = NULL; break; }

If fgets() returns NULL because of an end of file condition, you
are throwing away everything which has been previously read into
the buffer, just the same as if it were NULL because of a read
error. The test for feof() needs to be done here, not as the
loop condition. (As written, your code will work if and only if
the last character to be read in is a newline.)

> line = temp;
> if( (temp = strchr( line, '\n' ))){ temp[0] = 0; break; }
> buff_size += MALLOC_STEP;
> if( !(temp = realloc( line, buff_size )))
> { free( line ); line = NULL; break; }
> line = temp;
> index = strlen( line );

There is no real need for strlen() to determine how long the
string is. The only reason execution is still inside the while
loop is because no read error, end of file, or newline as been
encountered, which means the buffer was filled and the last
element of the buffer has been set to 0. That means you can
allocate MALLOC_STEP more space, and perform another fgets()
starting at (line + buff_size - MALLOC_STEP - 1).

In the example I provided earlier the only tricky part of using
the above offset to calculate the start point is to be sure to
initialize the size to 1 so that on the first pass it is
decremented to a 0 offset.

Note also that your code requires calling malloc() initially,
and the example does not. Otherwise, aside from other
formatting style issues, you might find that the construct
"&line[index]" would really be better expressed as a pointer
plus various offsets.

Floyd

Floyd Davidson

unread,
Apr 8, 2000, 3:00:00 AM4/8/00
to

These examples are still incorrect because one reason
that fgets() returns NULL is when an attempt is made to read
past the end of the file. In that case all examples below
are throwing away whatever data has already been read. That
would be appropriate for a read error, but not for end of file.

"Lou Zher" <ab...@localhost.com> wrote:
>Robert Jon Bredlau <rbre...@cats.ucsc.edu> wrote:
>>How's this:


>> temp = line;
>> line = fgets( &line[index], MALLOC_STEP, fpFile );

>> if( line == NULL ){ free( temp ); break; }
>> line = temp;
>
>For clarity, you might want to consider changing it to this:
>temp = fgets( &line[index], MALLOC_STEP, fpFile )
>if (temp == NULL) {


> free( line );
> line = NULL;
> break;
>}
>

>or just...
>
>if (fgets( &line[index], MALLOC_STEP, fpFile ) == NULL) {


> free( line );
> line = NULL;
> break;
>}
>

>The var that receives from alloc should never be modified until
>after free, and then only to set it to NULL.

As long as the value of the pointer is saved in another
variable, and it is that value which is given to free(), it
makes no difference if the variable is changed or not. The
original poster did not do that, and was trying to free a null
pointer, but that made little difference since the entire
snippet of code was incorrect anyway.

Mark McIntyre

unread,
Apr 9, 2000, 3:00:00 AM4/9/00
to
On 8 Apr 2000 22:48:52 GMT, rbre...@cats.ucsc.edu (Robert Jon
Bredlau) wrote:

>Well, I finally got it to work. Here's the finished function if anyone wants
>to use it. I'm not sure how fool proof it is but it did read in a line with
>over 40,000 characters.

<snip>


> line = fgets( &line[index], MALLOC_STEP, fpFile );

> if( line == NULL ){ free( line ); line = NULL; break; }

This isn't going to work. If line is NULL you lost the pointer, ie you
have leaked memory. Then you free the NULL pointer (safe but
pointless) then you set a NULL pointer to NULL. Also safe but
pointless...


Mark McIntyre

C- FAQ: http://www.eskimo.com/~scs/C-faq/top.html

Robert Jon Bredlau

unread,
Apr 9, 2000, 3:00:00 AM4/9/00
to
Mark McIntyre (ma...@garthorn.demon.co.uk) wrote:
: On 8 Apr 2000 22:48:52 GMT, rbre...@cats.ucsc.edu (Robert Jon
How's this:
temp = line;
line = fgets( &line[index], MALLOC_STEP, fpFile );
if( line == NULL ){ free( temp ); break; }
line = temp;

I love you guys!
8)
:
:
: Mark McIntyre
:
: C- FAQ: http://www.eskimo.com/~scs/C-faq/top.html

Lou Zher

unread,
Apr 9, 2000, 3:00:00 AM4/9/00
to
Robert Jon Bredlau <rbre...@cats.ucsc.edu> wrote in message
news:8cojua$k...@darkstar.ucsc.edu...

>How's this:
> temp = line;
> line = fgets( &line[index], MALLOC_STEP, fpFile );
> if( line == NULL ){ free( temp ); break; }
> line = temp;

For clarity, you might want to consider changing it to this:
temp = fgets( &line[index], MALLOC_STEP, fpFile )
if (temp == NULL) {


free( line );
line = NULL;
break;
}

or just...

if (fgets( &line[index], MALLOC_STEP, fpFile ) == NULL) {


free( line );
line = NULL;
break;
}

The var that receives from alloc should never be modified until after free,


and then only to set it to NULL.

-LZ

Lou Zher

unread,
Apr 9, 2000, 3:00:00 AM4/9/00
to
Floyd Davidson <fl...@ptialaska.net> wrote in message
news:87purzn...@barrow.com...

>
> These examples are still incorrect because one reason
> that fgets() returns NULL is when an attempt is made to read
> past the end of the file. In that case all examples below
> are throwing away whatever data has already been read. That
> would be appropriate for a read error, but not for end of file.

The snippnet sits in a feof checking loop. The free & break are only
occuring on error.

From: rbre...@cats.ucsc.edu (Robert Jon Bredlau)
Message-ID: <8cod0k$j...@darkstar.ucsc.edu>
>while( !feof( fpFile ))
> {
> temp = line;
> line = fgets( &line[index], MALLOC_STEP, fpFile );

> if( line == NULL ){ free( line ); line = NULL; break; }
> line = temp;

Floyd Davidson <fl...@ptialaska.net> wrote in message
news:87purzn...@barrow.com...


>"Lou Zher" <ab...@localhost.com> wrote:
>>For clarity, you might want to consider changing it to this:

[snip]


>>
>>The var that receives from alloc should never be modified until
>>after free, and then only to set it to NULL.
>

> As long as the value of the pointer is saved in another
> variable, and it is that value which is given to free(), it
> makes no difference if the variable is changed or not. The
> original poster did not do that, and was trying to free a null
> pointer, but that made little difference since the entire
> snippet of code was incorrect anyway.

Perhaps the superlative /never/ in my statement was a bit harsh. It would be
like playing musical chairs with the file handles/streams. I just consider
it bad form. You open it? you close it. You alloc it? you free it. To the
compiler it makes no difference. In run-time it makes no difference. At
debug time, it might.

-LZ

Floyd Davidson

unread,
Apr 9, 2000, 3:00:00 AM4/9/00
to
"Lou Zher" <ab...@localhost.com> wrote:

>Floyd Davidson <fl...@ptialaska.net> wrote:
>>
>> These examples are still incorrect because one reason
>> that fgets() returns NULL is when an attempt is made to read
>> past the end of the file. In that case all examples below
>> are throwing away whatever data has already been read. That
>> would be appropriate for a read error, but not for end of file.
>
>The snippnet sits in a feof checking loop. The free & break are only
>occuring on error.
...

>>while( !feof( fpFile ))
>> {
>> temp = line;
>> line = fgets( &line[index], MALLOC_STEP, fpFile );
>> if( line == NULL ){ free( line ); line = NULL; break; }
>> line = temp;

If previous calls to fgets() have read in some multiple of
MALLOC_STEP bytes which are exactly the last bytes in a file
which ends without a newline,

1) the test for feof() as the while-loop condition will be false,
2) the fgets() call will
a) find no input data to read
b) but will attempt to read past the end of the file,
c) and therefore will return NULL,
3) and therefore all buffered data will be discarded.

The appropriate place to test for feof() is immediately after a call
to fgets() which has returned a NULL, and based on feof() the existing
buffer should be discarded or not.

Floyd

Lou Zher

unread,
Apr 9, 2000, 3:00:00 AM4/9/00
to
Floyd Davidson <fl...@ptialaska.net> wrote in message
news:87d7nzm...@barrow.com...

> If previous calls to fgets() have read in some multiple of
> MALLOC_STEP bytes which are exactly the last bytes in a file
> which ends without a newline,
>
> 1) the test for feof() as the while-loop condition will be false,
> 2) the fgets() call will
> a) find no input data to read
> b) but will attempt to read past the end of the file,
> c) and therefore will return NULL,
> 3) and therefore all buffered data will be discarded.
>
> The appropriate place to test for feof() is immediately after a call
> to fgets() which has returned a NULL, and based on feof() the existing
> buffer should be discarded or not.

Whoops... you're correct. It's actually worse than that though. Files ending
in newline, then EOF cause feof to miss it, but fgets to fail from eof.

Your fix works.

-LZ

Floyd Davidson

unread,
Apr 9, 2000, 3:00:00 AM4/9/00
to
"Lou Zher" <ab...@localhost.com> wrote:
>Floyd Davidson <fl...@ptialaska.net> wrote:
>> If previous calls to fgets() have read in some multiple of
>> MALLOC_STEP bytes which are exactly the last bytes in a file
>> which ends without a newline,
>>
>> 1) the test for feof() as the while-loop condition will be false,
>> 2) the fgets() call will
>> a) find no input data to read
>> b) but will attempt to read past the end of the file,
>> c) and therefore will return NULL,
>> 3) and therefore all buffered data will be discarded.
>>
>> The appropriate place to test for feof() is immediately after a call
>> to fgets() which has returned a NULL, and based on feof() the existing
>> buffer should be discarded or not.
>
>Whoops... you're correct. It's actually worse than that though. Files ending
>in newline, then EOF cause feof to miss it, but fgets to fail from eof.

That is why in my original article pointed out that the
conditional for the while-loop using feof() would never indicate
an eof condition and should be replaced with a "while (1) {...}"
loop.

Floyd

>Your fix works.

0 new messages