Search code examples
c++iostreamifstream

The book "Accelerated C++: Practical Programming by Example". End-of-file indictation misunderstanding


The authors explain how to write a function which reads grades from standard input and puts them into a vector, but they do it in a confusing fashion. I'll put here an excerpt that confuses me:

... we don’t quite know when to stop. We can keep reading grades until we can no longer do so, but at that point we have a problem.

There are two reasons why we might no longer be able to read a grade:
We might have reached end-of-file, or we might have encountered something that is not a grade.

In the first case, our caller will think that we have reached end-of-file. This thought will be true but misleading, because the end-of-file indication will have occurred only after we have successfully read all the data.
Normally, an end-of-file indication means that an input attempt failed.

In the second case, when we have encountered something that isn’t a grade, the library will mark the input stream as being in failure state, which means that future input requests will fail, just as if we had reached end-of-file.

Therefore, our caller will think that something is wrong with the input data, when the only problem was that the last homework grade was followed by something that was not a homework grade.

In either case, then, we would like to pretend that we never saw whatever followed the last homework grade.

Such pretense turns out to be easy:
If we reached end-of-file, there was no additional input to read; if we encountered something that wasn’t a grade, the library will have left it unread for the next input attempt.

Therefore, all we must do is tell the library to disregard whatever condition caused the input attempt to fail, be it end-of- file or invalid input.
We do so by calling in.clear() to reset the error state inside in, which tells the library that input can continue despite the failure.

I understand the part that tells that there are two reasons that make us unable to read data.

The first is the end-of-file, and the second is a piece of data that cannot be treated as a double.

And now, the authors try to go deeper into these two cases which confuses me. They say that we need to read all the data to get the end-of-file indication, and right after that, they say that any fail of getting input, leads to the end-of-file indication. It seems like the authors contradict themselves.

I put a bit more text here, so you have some context, and I need a more clear and understandable rewrite.


Solution

  • The point of confusion, here, is (most likely) with the phrase, "just as if we had reached end-of-file" (although, I agree, the whole paragraph is somewhat mumbly and meandering).

    When using a std::ifstream object (or one from a derived/related class), there are a number of conditions that can cause an attempted read operation to fail: one is that the end-of-file has been reached (in which case we're done); another is that bad input data – i.e. a character or characters that cannot be interpreted as a double (say) – is encountered in the file stream.

    In the latter case, the read will fail and a flag (typically the failbit or badbit members) will be set. In the former case, the eofbit member will (also) be set.

    We can call the .fail() member function of the stream object to test if either of those situations has occurred, and we can call the .eof() member function to test specifically if the end-of-file has been reached.

    If fail() returns true but eof() returns false, then we can call the .clear() function to reset/clear the flags, after which we can continue reading (or trying to read) more data. We can continue this read/test/clear cycle until we get an actual EOF.