The end-of-file indicator that is checked by feof()
is only set after a previous file I/O operation has failed. You must attempt an I/O operation to find out if you have reached the end of the file. So, with an empty file, your code attempts to read the file, the end-of-file indicator is set, no values are read into the first struct
, but idx
is incremented, so it looks like you have added data for a player. But the fields of the first struct
are uninitialized, so you are seeing garbage. Also note that dota[idx].name
is presumably an array of char
s, so it decays to a pointer to char
when passed to fscanf()
. Using &dota[idx].name
, as you have, is wrong, though it might appear to work. It does cause the compiler to emit a warning, and you should have these enabled (I always use at least gcc -Wall -Wextra -pedantic
).
You should not use feof()
to control file I/O loops. One simple solution is to use the return value of fscanf()
to control the loop:
while(fscanf(fp, "%[^ ] %d %d\n",
dota[idx].name, &dota[idx].score, &dota[idx].num) == 3) {
idx++;
}
This will only update a player
struct
if three assignments are made by the call to fscanf()
. But, the problem with this simple solution is that it doesn't handle malformed input well. If a line of the data file is missing a field, the struct
will be incorrectly filled, and the loop will terminate, even if there are more lines in the file to read. Also, since no field width is specified for the string conversion, a long name could crash your program.
A better solution uses fgets()
to read the lines of the file into a buffer, and then uses sscanf()
to extract the information from the buffer:
#include <string.h> // for strcpy()
...
char buffer[1000];
int line = 0;
char temp_name[100];
int temp_score, temp_num;
while(fgets(buffer, sizeof(buffer), fp)) {
++line;
if (sscanf(buffer, "%99[^ ] %d %d\n",
temp_name, &temp_score, &temp_num) == 3) {
strcpy(dota[idx].name, temp_name);
dota[idx].score = temp_score;
dota[idx].num = temp_num;
++idx;
} else if (buffer[0] != '\n') {
fprintf(stderr, "Bad input in line %d\n", line);
}
}
Here, a generous buffer
is declared to accept a line of text from the file, and temporary variables are declared to hold the values to be stored in the struct
fields. I have chosen a size of 100 for temp_name
, but this should match the size of the string array in your actual struct
. Note that the string conversion in sscanf()
has a field width of 99, so that at most 99 non-space (not non-whitespace, and why aren't you just using %99s
here?) characters are matched, leaving space for the '\0'
to be added.
fgets()
will return NULL
when it reaches the end of the file, so the loop will continue until that happens. For each line that is read, a line
counter is incremented. Then sscanf()
is used to read data into the temporary variables. The value returned from sscanf()
is checked to be sure that 3 assignments were made, and if so, then the data is copied into the struct
, and idx
is incremented. Note that strcpy()
is needed to copy the string from temp_name
to dota[idx].name
.
If the value returned from sscanf()
indicates that something other than 3 assignments were made, there is a check to see if buffer
holds an empty line. If not, an error message is printed to stderr
providing the line number of the bad input in the file.
A couple of further comments. Your do
loop appears to be missing the associated while()
. And you use fflush(stdin)
after the scanf()
inside the do
loop. fflush()
is meant to flush output streams. The behavior of fflush()
on input streams is explicitly undefined in the C Standard (ISO/IEC 9899:2011 7.21.5.2/2), though I believe that Microsoft deviates from the Standard here. Nevertheless, you should not use fflush(stdin)
in portable C code. Instead, use something like this:
int c;
...
scanf("%d", &choose);
while ((c = getchar()) != '\n' && c != EOF)
continue; // discard extra characters
This code reads characters from the input stream until either a '\n'
or EOF
is reached, clearing any characters left behind by the previous call to scanf()
from the input stream.