Reputation: 707
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
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
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
Reputation: 3440
you are checking eof with eof. try to check like this
while( (eof = fgetc(fp)) != EOF)
Upvotes: 0