Rampal Chaudhary
Rampal Chaudhary

Reputation: 707

fscanf not working as expected

I am using fscanf in C++ as follows:

Here is my text file:

abcd
efgh

There is no space or new line after "efgh". Now here is my loop:

FILE* fp;
char eof = 0;
do
{
    char str[20];
    fscanf(fp, "%s", str);
    std::cout<<str<<std::endl;
}
while((eof = fgetc(fp)) != eof)

The output i expect is:

abcd
efgh

But the actual output i get is:

abcd
efgh
efgh

I debugged a found that after reading "efgh" the value read into eof is '\n' and not EOF. The environment is linux mint.I want to know why always the last string is read 2 times.Please advice

Upvotes: 1

Views: 767

Answers (3)

James Kanze
James Kanze

Reputation: 153899

[Following up on the comments of Christian Rau in another thread, I've changed my first point to correspond to what I now realize]

There are several problems with your code. Some of the most obvious are:

  • The condition at the end of your do...while has undefined behavior. In the expression eof = fgetc(fp)) != eof, you modify an object (eof), and you access it elsewhere in the expression other than to determine the value to be stored. As far as the standard is concerned, anything may happen, and in fact, different compilers will do different things.

  • You are assigning the results of fgetc to a char, rather than to an int. The return value of fgetc is either in the range [0...UCHAR_MAX] or is EOF (which is guaranteed to be negative). In other words, it can take on one more value than will fit in a char. The results of then comparing the char with EOF depend on whether plain char is signed or not. If it's not signed, it can never have a negative value, and thus, will never be equal to EOF. If it's signed, then on particular character code (0xFF, or 'ÿ' in latin-1) will be detected as end of file. The return value of fgetc should always be assigned to an int, and the conversion to char should only be done after the test for EOF.

  • You're using the results of fscanf without checking that the function has succeeded. In C++, IO, be it iostream or FILE* is not predictive. Because of the way the interface is defined, it is impossible to tell in advance whether you will encounter end of file. You must try to read, and then test whether the read succeeded.

  • You're using fscanf into a char[] without limiting the input length. This is a buffer overflow waiting to happen.

In C++, the most natural way fo writing what you're doing would be:

std::string word;
while ( anIStream >> word ) {
    //  ...
}

Using the older, C compatible streams, you would write:

char word[20];
while ( fscanf( fp, "%19s", word ) == 1 ) {
    //  ...
}

In both cases, the check for success controls the loop; in the case of the C interface, you use a format width specifier to ensure that you don't overrun the buffer. (And in both cases, you must define the variables being read outside the loop, even though you will only use them in the loop.)

Upvotes: 1

wallyk
wallyk

Reputation: 57764

The last string is not being read twice. The problem is the loop's test for continuing:

(eof = fgetc(fp)) != eof

This assigns fgetc()'s return value to eof and checks that it is not equal to eof. Something which is hard to do logically. However, when fgetc() is called when the file is at EOF, it returns -1. That is cast to a char for the assignment, but the sub-expression in parenthesis retains the value -1 (due to type promotion rules). Comparing -1 to 255 or -127 (depending on whether char is signed or unsigned) finally terminates the loop.

The third time through the loop, fscanf() fails and does not update str: that's why the same value seems to have been read twice.

To fix it, the most straightforward technique would be:

do {
 ...
} while (!feof (fp));

However, on many operating systems, feof() doesn't work very well with fscanf() because the end of file indication isn't reliably set until fscanf() fails. A more reliable, O/S-resistent technique is to use

do {
    int result = fscanf (fp, ...whatever...);
    if (result < 0)   // end of file or i/o error?
         break;
} while (!feof (fp));

Upvotes: 2

Nikson Kanti Paul
Nikson Kanti Paul

Reputation: 3440

you are checking eof with eof. try to check like this

while( (eof = fgetc(fp)) != EOF)

Upvotes: 0

Related Questions