You have several problems:
- The while() loop isn't terminating (your initial question).
- fscanf() is unsafe - there are better alternatives.
- You're using fscanf() incorrectly.
- Reading a string a character at a time is inefficient.
- Repeatedly calling "realloc()" is inefficient - there are better alternatives.
Here is some example code.
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#define MAX_STRING 80
typedef struct {
unsigned id;
char *lastName;
float grade;
} students_t;
students_t* enter_new_student (FILE *inputFile)
{
char buffer[MAX_STRING];
unsigned id;
int iret;
// Check for end of input
iret = fscanf(inputFile, "%u", &id);
if ((iret < 1) || feof(inputFile)) { // iret should be "1" if successful
return NULL;
}
// Allocate a record and read its data
students_t *student = (students_t *)malloc(sizeof(students_t));
iret = fscanf(inputFile, "%s %f", buffer, &student->grade); // iret should be "2" if successful
student->id = id;
student->lastName = strdup(buffer); // strdup() does an implicit "malloc()" and "strcpy()"
// Return new student
return student;
}
int main()
{
students_t *student = NULL;
int record_counter = 0;
FILE *fp;
// Open file
if (!(fp = fopen("tmp.txt", "r"))) {
perror("unable to open file");
return 1;
}
// Read student records
while ((student = enter_new_student(fp))) {
if (student) {
++record_counter;
printf("new student=%s,id=%u, grade=%f, record_counter=%d\n",
student->lastName, student->id, student->grade, record_counter);
}
}
// Done
printf("Done: final record count=%d\n", record_counter);
return 0;
}
Here is a sample "tmp.txt" file:
1 Johnson 4.0
2 Jackson 3.5
3 Jamison 3.85
And corresponding sample output:
new student=Johnson,id=1, grade=4.000000, record_counter=1
new student=Jackson,id=2, grade=3.500000, record_counter=2
new student=Jamison,id=3, grade=3.850000, record_counter=3
In general, prefer using fgets() over fscanf(): Disadvantages of scanf
Notice that I put everything having to do with reading a student record inside a separate function: enter_new_student()
. You'll also notice that the "control structure" - the "while loop" is OUTSIDE of the function.
There are two (related) conditions that can cause the loop to exit:
- Didn't read "id"
- End of file
The reason your original "while loop" failed was that fscanf()
will never return ' '
... so you inadvertently coded an "infinite loop". Here's why:
https://linux.die.net/man/3/fscanf
Return Value
These functions return the number of input items successfully matched and assigned, which can be fewer than provided
for, or even zero in the event of an early matching failure.
The value EOF is returned if the end of input is reached before either
the first successful conversion or a matching failure occurs. EOF is
also returned if a read error occurs, in which case the error
indicator for the stream (see ferror(3)) is set, and errno is set
indicate the error.