0

I wrote simple code in c to store student info ( roll number, name,course, fee, department) in a text file student.txt -- code snippet:

FILE *fp;
fp=fopen("student.txt","r");
//Input details from user and ..
//store it in student.txt
fprintf(fp,"%d %s %s %d %s ",s.rollno,s.name,s.course,s.fee,s.dept);

And I wrote following code to retrieve and print all records from file, AND it retrieve last record TWICE !

while (!feof(fp))
            {
                fscanf(fp,"%d%s%s%d%s",&s.rollno,s.name,s.course,&s.fee,s.dept);

                printf("%d %s %s %d %s\n",s.rollno,s.name,s.course,s.fee,s.dept);

            }

//OUTPUT : 
46 mustafa be 12000 cse 
41 Sam BE 32000 CSE 
42 Howard BE 25000 EE  
44 Sheldon BE 25000 CSE 
44 Sheldon BE 25000 CSE

Why last record(Sheldon..) is read twice from file (though its written only once in file, I checked). Please help, really stuck.

Mustafa
  • 123
  • 1
  • 1
  • 8

1 Answers1

0

The EOF indicator for a stream only gets set after you attempt to read beyond the end of the file. So a test with feof() won't work unless you've already attempted to go too far.

You can see this behaviour in the ISO C11 standard where it states for fgetc:

If the end-of-file indicator for the stream is set, or if the stream is at end-of-file, the end-of-file indicator for the stream is set and the fgetc function returns EOF.

In other words, the first time the EOF flag is set for a stream is when you try to read the first character beyond the last in the file.

What's happening in your case is that the file pointer is at the end of the file, having just successfully read the final character. It's a little more complex than that given the ability of fscanf() to skip leading whitespace and so on but, basically, the next fscanf() will read beyond end of file before it scans any items.

And, when you're at the end of the file, feof() is not yet true. Your code will then attempt fscanf which will fail (and set the EOF flag) and printf will output the previous contents again (since fscanf did not change them).

Since fscanf returns the number of items successfully scanned, you could opt for something like:

while (fscanf (fp, "%d%s%s%d%s", blah, blah) == 5) {
    printf (blah, blah);
}
// check feof here to deside if EOF or bad input line.

See the following program for a full example:

#include <stdio.h>

int main (void) {
    int rollno, fee;
    char name[100], course[100], dept[100];
    FILE *fp = fopen ("qq.in", "r");
    if (fp == NULL) {
        puts ("Cannot open file");
        return 1;
    }
    while (fscanf (fp, "%d%s%s%d%s", &rollno, name, course, &fee, dept) == 5) {
        printf ("%d %s %s %d %s\n", rollno, name, course, fee, dept);
    }
    fclose (fp);
    return 0;
}
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Wow! crystal clear. Thanx :) – Mustafa Jan 12 '14 at 04:15
  • Just 2 doubts here, and I can move on satisfied : 1. When the file pointer is at the end of the file, why feof() is not true? Technically it should be coz there is not even space after last character. 2. what does fscanf() actually returns? Something to do with number of format specifiers used? – Mustafa Jan 12 '14 at 04:20
  • @Mustafa: 1/ Because that's what the standard says :-) EOF is a flag set when you try to read _too much._ Even if it was set at file end, that wouldn't help here since fscanf may leave you at the final newline char, not at file end. Scanning skips leading whitespace for things like `%d`. 2/ `fscanf` gives you the number of items successfully converted so 5 is ideal here. 0 means either EOF or the first thing couldn't be decoded as a number with `%d`. – paxdiablo Jan 12 '14 at 04:25
  • Yup, I got it now :) Actually I misunderstood the EOF character as the last character(and i thought EOF should be set _at_ last character). But EOF is set when it try to read(i.e. try to read too much) after the last character. Right? – Mustafa Jan 12 '14 at 04:37