46
#include <iostream>
#include <fstream>

int main() {
    std::fstream inf( "ex.txt", std::ios::in );
    while( !inf.eof() ) {
        std::cout << inf.get() << "\n";
    }
    inf.close();
    inf.clear();
    inf.open( "ex.txt", std::ios::in );
    char c;
    while( inf >> c ) {
        std::cout << c << "\n";
    }
    return 0;
}

I'm really confused about eof() function. Suppose that my ex.txt's content was:

abc

It always reads an extra character and shows -1 when reading using eof(). But the inf >> c gave the correct output which was 'abc'? Can anyone help me explain this?

jww
  • 83,594
  • 69
  • 338
  • 732
Chan
  • 12,569
  • 40
  • 99
  • 152
  • Thanks for pointing out that. I was competing on TopCoder, and I usually import all the namespaces in order to save time. – Chan Dec 26 '10 at 07:57

4 Answers4

76

-1 is get's way of saying you've reached the end of file. Compare it using the std::char_traits<char>::eof() (or std::istream::traits_type::eof()) - avoid -1, it's a magic number. (Although the other one is a bit verbose - you can always just call istream::eof)

The EOF flag is only set once a read tries to read past the end of the file. If I have a 3 byte file, and I only read 3 bytes, EOF is false, because I've not tried to read past the end of the file yet. While this seems confusing for files, which typically know their size, EOF is not known until a read is attempted on some devices, such as pipes and network sockets.

The second example works as inf >> foo will always return inf, with the side effect of attempt to read something and store it in foo. inf, in an if or while, will evaluate to true if the file is "good": no errors, no EOF. Thus, when a read fails, inf evaulates to false, and your loop properly aborts. However, take this common error:

while(!inf.eof())  // EOF is false here
{
    inf >> x;      // read fails, EOF becomes true, x is not set
    // use x       // we use x, despite our read failing.
}

However, this:

while(inf >> x)  // Attempt read into x, return false if it fails
{
    // will only be entered if read succeeded.
}

Which is what we want.

Thanatos
  • 37,926
  • 14
  • 76
  • 136
  • Thanks Thanatos. I think I get it now ;) – Chan Dec 26 '10 at 08:04
  • 3
    Probably should not use >> instead of get(). Remember that the >> operator always skips proceeding white space before attempting to read the next value while get() reads every character one at a time. – Martin York Dec 26 '10 at 08:09
  • @Thanatos: Sorry to bother you but ... What about streams? You said that EOF is set when we try to read characters past the end of the file but **what happens when we try to `peek()` at the input stream `cin` when it has no characters?** The standard says that `traits::eof()` is called but **peek() seems to wait for an input** when I attempt to use it that way. – chosentorture Jul 11 '13 at 16:06
  • `peek` will return EOF if you attempt to peek past the end of the input. However, `cin` is typically a terminal that you're typing into. (This seems to be your case.) "Waiting for user input" is not the same as an EOF, it's just the source of data (you) is slow. So `peek` blocks, waiting for you to give it input. If you input EOF (Ctrl+D on Linux), then `peek` will return, and `eof` should be true. Otherwise, it returns what you typed. – Thanatos Aug 23 '13 at 22:37
  • I have a question. The doc says that the [bool operator](http://www.cplusplus.com/reference/ios/ios/operator_bool/) is defined as ![fail()](http://www.cplusplus.com/reference/ios/ios/fail/), so a EOFed stream should be interpreted as true? How comes that it will return false? – Zesen Qian Dec 25 '15 at 15:46
  • Question solved. In the failed() link given above, `Reaching the End-of-File sets the eofbit. But note that operations that reach the End-of-File may also set the failbit if this makes them fail (thus setting both eofbit and failbit).` – Zesen Qian Dec 25 '15 at 15:49
7

The EOF flag is only set after a read operation attempts to read past the end of the file. get() is returning the symbolic constant traits::eof() (which just happens to equal -1) because it reached the end of the file and could not read any more data, and only at that point will eof() be true. If you want to check for this condition, you can do something like the following:

int ch;
while ((ch = inf.get()) != EOF) {
    std::cout << static_cast<char>(ch) << "\n";
}
Justin Spahr-Summers
  • 16,742
  • 2
  • 59
  • 78
  • 5
    "The EOF flag is only set after a read operation reaches the end of the file." is exactly the statement that causes confusing about EOF. The EOF flag is only set *after a read operation attempts to read past the end of the file*. The last bit is critical: If I read 3 bytes from a file 3 bytes long, EOF is false, until I attempt another read. – Thanatos Dec 26 '10 at 07:31
  • iostream *doesn't* return an EOF constant. The return value of `int istream::get()` appears to be undefined at the end of the file -- you have to check `istream::eof()` – Ken Bloom Dec 26 '10 at 07:36
  • @Ken Bloom: Are you sure? My standard says "Returns: `c` if available, otherwise `traits::eof()`." 27.6.1.3 (Draft 2, 2 Dec 96) – Thanatos Dec 26 '10 at 07:45
  • You're right, but that's still a far cry from the `EOF` constant used by **C** stdio. I'm wrong. Somewhere out there in the definition of `char_traits` it says that `char_traits::eof()` returns `EOF` – Ken Bloom Dec 26 '10 at 07:48
  • @Ken Bloom Just found section 21.1.3.1 `struct char_traits`: "The member `eof()` returns `EOF`." – Justin Spahr-Summers Dec 26 '10 at 07:51
  • @Justin, I also just found it at the same time you did. – Ken Bloom Dec 26 '10 at 07:52
  • @Ken Bloom: My point wasn't so much that it might or might not return `EOF` - although, good find on that, I did not know that `EOF` and `char_traits::eof()` were gaurenteed to be equal - but that its return value was well defined to some constant somewhere. – Thanatos Dec 26 '10 at 07:55
  • @Justin Spahr-Summers: Thanks for a another great solution ;) – Chan Dec 26 '10 at 08:07
  • You should still not test for EOF in the condition. What happens if something else goes wrong in the stream and the bad bit gets set. Then the stream will never return EOF. – Martin York Dec 26 '10 at 08:10
  • 1
    @Martin York The EOF *flag* might not be set, but `get()` will still return `traits::eof()`, which is used as a general error code if the read fails. – Justin Spahr-Summers Dec 26 '10 at 08:12
  • @Ken Bloom: where can I find the standard for C++ to reference? Thanks! – Chan Dec 26 '10 at 08:23
  • @Chan: you have to buy the standard, but most of us look at the [N1905 working draft](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf). – Ken Bloom Dec 26 '10 at 17:08
7

iostream doesn't know it's at the end of the file until it tries to read that first character past the end of the file.

The sample code at cplusplus.com says to do it like this: (But you shouldn't actually do it this way)

  while (is.good())     // loop while extraction from file is possible
  {
    c = is.get();       // get character from file
    if (is.good())
      cout << c;
  }

A better idiom is to move the read into the loop condition, like so: (You can do this with all istream read operations that return *this, including the >> operator)

  char c;
  while(is.get(c))
    cout << c;
Ken Bloom
  • 52,354
  • 11
  • 101
  • 164
  • Should "`istream` read operations ... including the `<>` operator" ? – Thanatos Dec 26 '10 at 07:47
  • @Thanatos: yeah. That's what I get for posting late at night. – Ken Bloom Dec 26 '10 at 17:09
  • 2
    You want `!is.fail()` instead of `is.good()`. When reading, e.g., a number from a file, the stream has to reach the end of the file before determining if it read the last digit of the number. In that case, a number will have been successfully read but the end of file will be reached, causing `is.good()` to return false. This could cause you to skip the last value on the file. Instead, checking for `!is.fail()` will do a right thing in all cases. – binki Mar 12 '18 at 00:54
  • @KenBloom - If the stream in your while(is.get(c)) is binary, won't this prematurely terminate the loop if the value of the byte is 0 (a completely expected value for a binary file?) Wouldn't it be better to use the while(n=is.get() != Traits::eof()) for a binary file? – SMGreenfield Jul 09 '19 at 07:02
  • 1
    @smgreenfield: the overload of get that takes an output parameter returns an istream& for further chaining or error reporting. – Ken Bloom Jul 11 '19 at 09:36
2

eof() checks the eofbit in the stream state.

On each read operation, if the position is at the end of stream and more data has to be read, eofbit is set to true. Therefore you're going to get an extra character before you get eofbit=1.

The correct way is to check whether the eof was reached (or, whether the read operation succeeded) after the reading operation. This is what your second version does - you do a read operation, and then use the resulting stream object reference (which >> returns) as a boolean value, which results in check for fail().

zeuxcg
  • 8,703
  • 1
  • 22
  • 33