1

I have a programming assignment for a C++ course involving streams and I'm trying to gain a better understanding as to why some of the functions work the way they do.

I am reading input from an istringstream with whitespace after the text. Why does the last word get repeated in the output?

istringstream is;
string inputstring = "The cliched statement regarding the big brown dog and foobar or something    ";
string outputstring;
is.str(inputstring);
while (is.good())
{
    is >> outputstring;
    cout << outputstring << endl;
}

So, instead of looping on the good flag, I am now doing the extraction as the while condition:

while (is >> outputstring)
...

This works well and doesn't repeat the last word. What is it about this statement that breaks out of the while loop when it is done reading? The extraction returns a reference to the same stream, but does it check flags or something?

Is there a single header that allows you to include all the streams?

Coding Mash
  • 3,338
  • 5
  • 24
  • 45
duffsterlp
  • 347
  • 1
  • 5
  • 15

3 Answers3

2

What is it about this statement that breaks out of the while loop when it is done reading? The extraction returns a reference to the same stream, but does it check flags or something?

You're absolutely right that the extraction operator returns a reference to a stream. Any expression used as the control condition of a while loop (also for and do-while loop and if statement) is coerced to bool type. It counts as an explicit conversion, so ios_base::operator bool() is called, which returns !this->fail(). And that's how the flags get checked.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Thanks for the help! How does this account for EOF though? fail() only checks the bad bit and fail bit, it doesn't check for eof. Also, is the bool() function documented in the documentation for ios_base? I don't see it anywhere... – duffsterlp Sep 30 '12 at 17:15
  • @user1590960: `failbit` will be set if extraction fails as a result of being at the end of the stream. i.e. if some data is read and the end is reached, it succeeds and `eofbit` is set, so `good()` is false and `fail()` is also false. So it processes the data and loops and tries again. Now, no data is read, and `failbit` is set, which ends the loop. – Ben Voigt Sep 30 '12 at 17:28
1

Why does the last word get repeated in the output?

This is what happens: Because you have spaces at the end of inputstring, reading the last word does not flip the good() bit. The program finds a space character after the last word, thinks there are more words, so is.good() still returns true. is.good() only returns false if the program has tried to read past the end of the string; this has not happened yet.

Then, the next time you go through the loop and read from is, the program realizes that there is no more words, does not overwrite outputstring, and so you write once more what was in outputstring before, which is the last word.

try printing the result of is.good() before and after you read from is, and remove the space characters at the end of inputstring to see what changes…

user711413
  • 761
  • 5
  • 12
  • You now have enough reputation to leave comments on other people's answers. I happen to believe you are correct about `.good()` (although Dietmar is a real expert who I wouldn't expect to make such a mistake) but this *is not a real answer*. – Ben Voigt Sep 30 '12 at 16:44
0

You always need to check after reading because the stream may be in a good state but trying to read something could fail: after all, the stream can't know what you are about to read. Thus, when you check the stream after the last element, it is still good(). Then you try to read which fails and good() is false but you print the value anyway.

Also, you don't want to use .good() as a condition as this may turn to false even after a read was successful, if the end of the stream was reached. For example, if a number is read and there is no whitespace following the number at the end of the file, then std::ios_base::eofbit is set. Just use the idomatic loop like this:

while (in >> value) {
    std::cout << "read value='" << value << "'\n";
}

It looks far too simple to be correct but actually does the Right Thing!

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Actually, `eofbit` doesn't get set on the last successful read when the stream ends with whitespace, because then the whitespace ends the prior extraction operation, and then the next one skips over the whitespace and can't find any content, so the stream goes directly to failed state. If there's no trailing whitespace (no newline character on the last line of the input), then the extraction operation is ended by the EOF condition and `good()` becomes false as a result of the successful read. – Ben Voigt Sep 30 '12 at 16:52
  • This is what I meant: there may be a successful read but `std::ios_base::eofbit` gets set because the extraction only terminated when reaching the end of the file. A value not followed by whitespace is an example of this. Using `std::getline()` without a terminating newline is another example. I have tried to clarify the answer. – Dietmar Kühl Sep 30 '12 at 16:58
  • @BenVoigt: That makes sense. One more question, in the last extraction, why wouldn't the stream write an empty string to the string variable? I'd expect that an EOF would be like an empty string or something... – duffsterlp Sep 30 '12 at 17:21
  • @user1590960: That's just not part of the contract for `operator>>(basic_ostream, string)`. It skips whitespace until it finds a non-whitespace character, then reads all the consecutive non-whitespace characters. If there is no non-whitespace, the first step fails. – Ben Voigt Sep 30 '12 at 17:31
  • No. EOF is not set until you read past the end of file. Reading the last number takes you up-to but not past the end of file and thus EOF is not set until your next attempt to read. – Martin York Sep 30 '12 at 18:53