I asked a different question about this earlier, but I was way off base about the problem so I've created a new question as I'm asking an entirely different question.
I have a function that reads a given line in a text file (given by ac variable). It performs the read of the line and then checks if that was the last line in the file. If so it increments a value.
The problem is that it's incremented the value even when it's not the actual end of the file. I think I'm using feof wrong but I've had no luck getting it to work:
int readIn(TinCan* inCan, int toggle)
{
int ii, isFinished = 0;
char fullName[20];
sprintf(fullName, "Label_%d.txt", inCan->pid);
FILE* fp;
fp = fopen(fullName, "r");
if(fp==NULL)
{
printf("Error: could not open %s\n", fullName);
}
else
{
for (ii=0; ii < ((inCan->ac)-1); ii++)
{
fscanf(fp, "%*d %*d %*d\n"); /*move through lines without scanning*/
}
fscanf(fp,"%d %d %d", &inCan->ac, &inCan->state, &inCan->time);
}
if (feof(fp) && (toggle == 1))
{
printf("File ended");
writeLog(inCan);
isFinished = 1;
terminated++;
}
fclose(fp);
return finished;
}
Sample data as requested, this is a text file I may use:
1 1 30
2 2 5
3 1 1
fscanf correctly assigns the values. On the second line, feof returns true and terminated is incremented. feof returns true again for the 3rd line and increments terminated a second time.
feof() does not detect if the file has ended. It detects if the last read error was due to the file having ended.
feof() only happens after a failed read.
So, first read data and check the return value. If the read failed use feof() to make sure it failed because the END-OF-FILE was reached (other reasons for the read to fail are error of some kind (network down, bad sector, printer on fire, ...), detectable with ferror()).
It's hard to tell without knowing the data format, but
fscanf(fp,"%d %d %d", &inCan->ac, &inCan->state, &inCan->time);
will read 3 values, but on the last line, it won't have read the end of line character, so it's not the end of the file.
Try:
fscanf(fp,"%d %d %d\n", &inCan->ac, &inCan->state, &inCan->time);
Related
I read here that feof or more precisely using !feof in searching for a info in a file is a bad habit.
What I understood is that it's bad because it reads information from the FILE pointer before called function or process or something like that.
Wouldn't it be fine to have a do/while loop with fscanf inside and !feof as the exit condition?
This is a search function that I did:
typedef struct
{
char lname[20] , fname[20];
int nchildren;
}employee;
void searchemployee(char *filename , char *str)
{
employee e;
FILE *f;
int c;
f = fopen(filename, "r");
if (f == NULL)
printf("file couldn't be loaded\n");
else {
c = 0;
do {
fscanf(f, "%s %s %d\n", e.fname, e.lname, &e.nchildren);
if (strcmp(e.fname, str) == 0)
c = 1;
} while (c == 0 && !feof(f));
if (c != 1)
printf("employee not found\n");
else
printf("employee : %s %s| children : %d\n", e.fname, e.lname, e.nchildren);
}
fclose(f);
}
The return value of the function feof specifies whether a previous input operation has already encountered the end of the file. This function does not specify whether the next input will encounter the end of the file.
The problem with
do{
fscanf(f,"%s %s %d\n",e.fname,e.lname,&e.nchildren);
if (strcmp(e.fname,str)==0)
c=1;
}while(c==0 && !feof(f));
is that if fscanf fails and returns EOF due to encountering the end of the file, then it will write nothing to e.fname.
If this happens in the first iteration of the loop, then the content of e.fname will be indeterminate and the subsequent function call strcmp(e.fname,str) will invoke undefined behavior (i.e. your program may crash), unless e.fname happens to contain a terminating null character.
If this does not happen in the first iteration, but rather in a subsequent iteration of the loop, then the content of e.fname will contain the content of the previous loop iteration, so you will effectively be processing the last successful call of fscanf twice.
In this specific case, processing the last successful call of fscanf twice is harmless, except for being a slight waste of CPU and memory resources. However, in most other cases, processing the last input twice will result in the program not working as intended.
See the following question for further information:
Why is “while( !feof(file) )” always wrong?
If you change the loop to
for (;;) {
fscanf(f,"%s %s %d\n",e.fname,e.lname,&e.nchildren);
if ( c != 0 || feof(f) )
break;
if (strcmp(e.fname,str)==0)
c=1;
}
so that the loop condition is checked in the middle of the loop, then the problem mentioned above will be gone.
However, it is generally better to check the return value of fscanf instead of calling feof, for example like this:
c = 0;
while ( c == 0 && fscanf(f,"%s %s %d\n",e.fname,e.lname,&e.nchildren) == 3 ) {
if (strcmp(e.fname,str)==0)
c=1;
}
Also, you don't need the flag variable c. I suggest that you incorporate the lines
if (c!=1)
printf("emplyee not found\n");
else
printf("employee : %s %s| children : %d\n",e.fname,e.lname,e.nchildren);
partially into the loop, like this:
void searchemployee( char *filename, char *str )
{
employee e;
FILE *f = NULL;
//attempt to open file
f = fopen( filename, "r" );
if ( f == NULL )
{
printf( "file couldn't be loaded\n" );
goto cleanup;
}
//process one employee record per loop iteration
while ( fscanf( f, "%s %s %d\n", e.fname, e.lname, &e.nchildren ) == 3 )
{
//check whether we found the target record
if ( strcmp(e.fname,str) == 0 )
{
printf(
"employee : %s %s| children : %d\n",
e.fname, e.lname, e.nchildren
);
goto cleanup;
}
}
printf( "employee not found.\n");
cleanup:
if ( f != NULL )
fclose(f);
}
Another issue is that when using %s with scanf or fscanf, you should generally also add a width limit, to prevent a possible buffer overflow. For example, if e.fname has a size of 100 characters, you should use %99s to limit the number of bytes written to 99 plus the terminating null character.
Calling feof asks the question “Was end-of-file or an error encountered in a previous operation on this stream?”
If you use feof to answer that question, that is fine. But, you use feof to expect that your next operation will read data from the file, that is wrong. The previous operation might have ended just before the end of the file, so feof says “no,” but there is nothing left in the file to read.
The file/stream functions in the standard C library are designed to tell you when they failed because end-of-file was reached. You should use the return value (or other indication) provided by each function to test for a problem:
if (3 != fscanf(f, "%s %s %d\n", e.fname, e.lname, &e.nchildren))
{
// Handle fact that fscanf did not read and convert 3 values.
}
int x = getchar();
if (x == EOF)
{
// Handle fact that fscanf did not read and convert 3 values.
}
Note that calling fscanf and then feof will tell if fscanf encountered end-of-file or an input error, but it will not tell you whether fscanf read some input and assigned some values but then encountered end-of-file and did not finish. If you are reading only one thing, you might get away with fscanf followed by feof, but a more sophisticated program may need to distinguish partial inputs.
I'm writing a C program which reads a text file line by line with a certain format to it.
I made a do { ... } while(!feof(file)); loop but it always loops one too many times. This is an issue because I have made it so that when my program expects to read something but gets nothing, it throws an error, so now it is throwing an error every time because it reaches the end of the file at the top of my loop.
I figured this is because the eof flag is triggered only once you try to fscanf something but there is nothing there. How can I fix this problem? Putting a final fscanf at the bottom doesn't work because if it's not at the end of the file, it will mess all the readings up and shift everything by one.
do {
read = fscanf(/*...*/);
if (read != 1)
{
return -1;
}
// Read grades
read = fscanf(/*...*/);
if (read != 3)
{
return -1;
}
// Read student kind
int student_kind = 0;
read = fscanf(/*...*/);
if (read != 1)
{
return -1;
}
if (student_kind < 0 | student_kind > 2)
{
printf("Invalid student kind");
return -1;
}
SCIPER sciper_teammate = 0;
read = fscanf(/*...*/);
if (read != 1)
{
return -1;
}
} while (!feof(file));
Since you are using fscanf():
ISO/IEC 9899:2017
§ 7.21.6.2 - 16 - The fscanf function returns the value of the macro EOF if an input failure occurs before the first conversion (if any) has completed. Otherwise, the function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure.
EOF is a macro with the value of -1, by itself it's not distinguishable as for the reasons why it occurs.
For this distinction § 7.21.6.2 - 19 recommends the use of feof() for end-of-file and ferror() for I/O error:
EXAMPLE 3 To accept repeatedly from stdin a quantity, a unit of measure, and an item name:
#include<stdio.h>
/*...*/
int count; floatquant;
charunits[21], item[21];
do {
count = fscanf(stdin, "%f%20sof%20s", &quant, units, item);
fscanf(stdin,"%*[^\n]"); //here discarding unread characters in the buffer
} while(!feof(stdin) && !ferror(stdin));
This should work in your case but personaly. I don't like this approach since if you input less values than what fscanf is expecting this will fail, normaly resulting in an infinite loop.
My approach when reading formated input, is to check the inputed values.
For a sample input of 2 integers you can do something like:
Live sample
#include <stdio.h>
int main()
{
int a, b;
FILE* file;
if(!(file = fopen("file.txt", "r"))){
return 1;
}
while(fscanf(file, "%d %d", &a, &b) == 2){ //read each 2 integers in the file, stop when condition fails, i.e. there are nothing else to read or the read input is not an integer
printf("%d %d\n", a, b);
}
}
This addresses all input failures and will end the cycle for I/O error, for EOF and for bad inputs.
This question already has answers here:
Why is “while( !feof(file) )” always wrong?
(5 answers)
Closed 5 years ago.
So I have read multiple posts on why feof doesn't work properly and they all utilize using a while(fscanf(...) == 1) to read to end of file, the problem I have is that I have temp values that are different for each loop, because it is reading each line processing it and then moving to next line. The code I currently have reads all the input properly but prints the last line twice. I was wondering if there was a better way to go about this instead of just doing a hack job and removing the last line processed, since it is processed twice.
void readInputFile(Customer customers[]) {
FILE *input = fopen("hw4input.txt", "r");
while (!feof(input)) {
char tempName[MAXNAMELEN];
int tempQuantity;
char tempItem[MAXNAMELEN];
double tempPrice;
fscanf(input, "%s %d %s $%lf", &tempName, &tempQuantity, &tempItem, &tempPrice);
printf("%s %d %s %.2lf\n", tempName, tempQuantity, tempItem, tempPrice);
}
printf("EOF\n");
fclose(input);
}
You cannot use feof() to detect end of file before attempting to read from the file. feof() will return the state of the end-of-file status only after a failed attempt at reading data from the file.
You should instead read values from the stream, with fscanf() for a quick and dirty throw away toy program, or with fgets() for a more robust parser:
void readInputFile(Customer customers[]) {
FILE *input = fopen("hw4input.txt", "r");
if (input != NULL) {
char name[1024];
int quantity;
char item[1024];
double price;
while (fscanf(input, "%1023s %d %1023s %lf", name, &quantity, item, &price) == 4) {
printf("%s %d %s %.2lf\n", name, quantity, item, price);
}
printf("EOF\n");
fclose(input);
} else {
printf("Cannot open input file\n");
}
}
I was wondering if there was a better way to go about this instead of just doing a hack job and removing the last line processed
Yes, there is.
Check the return value from fscanf in your code. The call will fail when you try to read past the end of the file.
You should be checking it anyway. There are even a lot of people who post here who will opine that you shouldn't use any of the *scanf() functions anyway because they're very difficult if not impossible to use in any robust way. There's almost always a way you can feed one of the *scanf() functions data that will cause problems.
I have the following simple program to read from a text file (num.txt). The text file has numbers 1 2 3 4 5 in each line. When I run the program, it prints 5 twice. Can anybody tell me why is this happening, and how to fix it? thanks in advance
int main(void)
{
int number;
FILE *file;
int i = 0;;
file = fopen("num.txt", "r");
while (!feof(file)){
fscanf(file, "%d", &number);
printf("%d\n", number);
}
return 0;
}
Here's my text file num.xtx
1
2
3
4
5
And here's the program output
1
2
3
4
5
5
There is an extra 5
From the man page of scanf family of functions,
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 is set, and errno is set to indicate the
error.
This means that the last successful fscanf call reads the last line from the stream file after which the while loop condition !feof(file) is true because the end of file condition is not met yet. This means the loop is executed one extra time and the previous value of the variable number is printed again.
Please read this - while(!feof(file)) is always wrong
You should check the return value of scanf instead of checking the end of file indicator on the file stream.
#include <stdio.h>
int main(void) {
int number;
FILE *file = fopen("num.txt", "r");
// check file for NULL in case there
// is error in opening the file
if(file == NULL) {
printf("error in opening file\n");
return 1;
}
// check if fscanf call is successful
// by checking its return value for 1.
// fscanf returns the number of input
// items successfully matched and assigned
while(fscanf(file, "%d", &number) == 1)
printf("%d\n", number);
return 0;
}
The second time fscanf failed and didn't write anything to number, that's why it's still 5 from the last time. To know if fscanf succeeded, you have to check its return value.
fscanf returns the number of arguments that it wrote. In your case, if it returns 1, it worked; if it returns 0, it didn't. This is what you should check instead of feof.
while (fscanf(file, "%d", &number) == 1)
{
printf("%d\n", number);
}
This question already has answers here:
Why is “while( !feof(file) )” always wrong?
(5 answers)
Closed 9 years ago.
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.
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;
}