1

I am struggling a bit with understand the file input stream in C++. I have a code snippet as follows:

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    ifstream in("x.txt");

    bool done = false;
    do {
        string input = "";
        getline(in,input);
        int x1;
        int x2;
        in >> x1;
        in >> x2;
        cout << input << " " << x1 << " " << x2 << endl;
        in.ignore();
        if(in.eof()) {
            done = true;
            cout << "reached eof" << endl;  
        }
    } while(!done);

    return 0;
}

With the file x.txt reading as follows

task1
12
1313
task2
13
1414
[blank line]

Note the intentional inclusion of the blank line at the end of the input file. All this means is that the enter/return key was pressed after typing '1414'.

My expected output is

task1 12 1313
task2 13 1414
reached eof

But in actuality, the output is

task1 12 1313
task2 13 1414
 13 1414
reached eof

I understand that pressing enter within an input file generates an implicit newline character, and before using a statement like getline(ifstream, string) we should ignore() that next newline character. With that being said, why is ifstream.eof() not evaluating to true even though I ignore() the implicit newline character after '1414'?

Edward
  • 6,964
  • 2
  • 29
  • 55

2 Answers2

2

The eof flag isn't set until after you try to read beyond the end of the file. You should instead check the result after reading from the stream, perhaps something like

while (getline(in, input) && in >> x1 >> x2) {
    cout << input << " " << x1 << " " << x2 << endl;
    in.ignore();
}
cout << "reached eof" << endl;  
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • He should probably ignore until `'\n'` as well, just in case there is extra white space at the end of the line. – James Kanze Dec 03 '14 at 15:52
0

At the end of the second loop the stream position is at the end, but does not set eofbit until you try to read more data. Let's consider what happens after the first two loops have run and you enter the loop for the third time:

You try to read a line:

    getline(in,input);

This reads nothing, because you've already reached the end of the stream, so it sets eofbit and because nothing was extracted also sets failbit. But you don't check if a line was read, or if input is non-empty, you just blindly continue

You declare two uninitialized variables:

    int x1;
    int x2;

Then try to read again:

    in >> x1;
    in >> x2;

That doesn't even try to read anything, because failbit is already set, but you don't check it here either. x1 and x2 are unchanged and remain uninitialized.

Then you print out the uninitialized integers, which happens to print out the values from the previous loop because that's what happens to be on the stack:

    cout << input << " " << x1 << " " << x2 << endl;

This does nothing because failbit is set:

    in.ignore();

Now finally you check the stream state, but it's far too late to prevent all the problems above:

    if(in.eof()) {

You should be checking that I/O operations succeed, not assuming they do and then finding out much later they that there's a problem when it's far too late to do anything about it.

The way to check I/O operations is to test the stream in a boolean context, e.g. if (in), you should not be checking for EOF because that doesn't get set until you get to the end of the stream and then try to read again.

Mike's answer shows the correct way to do it, but you should also read Why is iostream::eof inside a loop condition considered wrong?

Community
  • 1
  • 1
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • If he's already extracted the last character of the file (with the `ignore`), then the `eofbit` and `failbit` should be set in the `getline`, before the `in >> x1` is reached. – James Kanze Dec 03 '14 at 15:54
  • @JamesKanze, ah yes, I mistakenly ignored the ignore :) I did mention that it doesn't even try to do anything when `failbit` is set, I'll make that clearer – Jonathan Wakely Dec 03 '14 at 15:55