Tag Archives: iostreams

About iostream::eof()

The iostream::eof() function detects that the stream has reached the end of the file, and no more data can be read. However, it only reaches this state when an attempt is made to read after the end of the file, so it can’t be used to determine if there is more data to be read.

For example, consider the following program:

#include <iostream>
#include <fstream>

int main()
{
    char filename[] = "numbers.dat";
    std::ifstream is(filename);
    if (is) {
        int i;
        while (!is.eof()) {
            is >> i;
            std::cout << i << "\n";
        }
    }
    else {
        std::cerr << "Couldn't open " << filename << " for reading\n";
    }
}

Imagine that numbers.dat contains the following:

1 2 3 4 5

The program won’t work properly because the loop condition !is.eof() will still be true after reading the last 5, so a subsequent read and assignment will be made, when there are no more numbers left in the file. When I run it I get this result, with the 5 repeated at the end:

1
2
3
4
5
5

The most common, and a correct way to loop while reading a file is to check that the read operation has succeeded in the loop condition instead:


        int i;
        while (is >> i) {
            std::cout << i << "\n";
        }

This produces the expected output:

1
2
3
4
5

It works correctly because after the last 5 has been read, the extraction operation returns false, and so the loop body is not entered.

The precise reason for this is that after the last 5 has been read the next read operation will fail, and the stream enters a failed state (is.fail() would return true). The loop is checking the return value of the extraction operation, which returns the stream itself. iostream::operator void*() is called, which returns NULL when fail() returns true, and this NULL pointer is implicitly converted to boolean false, which ends the loop.

The eof() function isn’t useless however; you can use it to determine whether a reading loop has ended because EOF has been reached, or if something else has gone wrong.

Consider the following:

        int i;
        while (is >> i) {
            std::cout << i << "\n";
        }
        if (is.eof()) {
            std::cout << "End of file reached\n";
        }
        else {
            std::cerr << "Error reading\n";
        }

If I run this on the file of numbers, I get:

1
2
3
4
5
End of file reached

But if I run it on a file containing letters as well:

1 2 3 a b 4 5

I get this result:

1
2
3
Error reading

So using eof() has allowed me to tell that there was a problem reading. Without it, I might have thought there were only 3 numbers in the file.