I'm trying to study the C language and basically what I want to do is read a file and put it into a struct I created, and then later I'll be doing other things with the struct, but I want to get through the first part first. Let's say that I have a text file called captains.txt and the contents are:
picard 95
janeway 90
pike 15
(note that the last line is just 'pike 15')
So I created a program that's like this:
#include <stdio.h>
#include <stdlib.h> //for exit()
#include <string.h>
#include <ctype.h>
struct captain
{
char capName[10];
int number;
};
typedef struct captain captain;
int main()
{
FILE* file = fopen("captain.txt","r");
if (file == NULL)
{
printf("\nerror opening file");
exit(1);
}
else{
printf("\nfile is opened");
}
char buffer[50];
fgets(buffer,50,file);
while (!feof(file))
{
captain c;
sscanf(buffer, "%s %d", &c.capName, &c.number);
printf("\nc captain is: %s %d", c.capName, c.number);
fgets(buffer,50,file);
}
fclose(file);
return 0;
}
The output on my console is
file is opened
c captain is: picard 95
c captain is: janeway 90
Process returned 0 (0x0) execution time : 0.006 s
Press any key to continue.
Hence Captain Pike is missing in space... almost literally because when I add a new line to the text file that it becomes like this:
picard 95
janeway 90
pike 15
(note the newline after 'pike 15')
Then my output becomes correct. So I know that my program doesn't account for the lack of a newline at the end of the file... so how do I solve this?
Compare these two programs, one (mis)using feof()
and one not using it at all. The first corresponds closely to the code in the question — it ignores the return value from fgets()
to its detriment. The second only tests the return value from fgets()
; it has no need to use feof()
.
eof53.c
#include <stdio.H>
int main(void)
{
char buffer[256];
fgets(buffer, sizeof(buffer), stdin);
while (!feof(stdin))
{
printf("[%s]\n", buffer);
fgets(buffer, sizeof(buffer), stdin);
}
return 0;
}
eof71.c
#include <stdio.H>
int main(void)
{
char buffer[256];
while (fgets(buffer, sizeof(buffer), stdin) != NULL)
printf("[%s]\n", buffer);
return 0;
}
Given a data file abc
containing 3 bytes — 0x41 ('A'
), 0x42 ('B'
), 0x43 ('C'
) and no newline, I get the following results:
$ eof53 < abc
$ eof71 < abc
[ABC]
$
This was tested on MacOS Big Sur 11.6.6.
Note that fgets()
does not report EOF (by returning a null pointer) when reading the (only) incomplete line, but empirically, feof()
does report EOF — correctly, since the file input has ended, even though fgets()
did return a string (but not a line) of data.
As explained in the canonical Q&A while (!feof(file))
is always wrong!, using feof()
rather than testing the return value from the I/O functions leads to bad results.