I was extracting values from a stringstream in a loop, resetting the stringstream at the top of the loop every time the loop executed. However, the stringstream's >>
operator fails at the second iteration every time. This distilled version of the code reproduces the problem I'm having:
istringstream word;
string in;
int number;
while(cin >> in) {
word.str(in);
//Uncommenting either one of the following lines seems to solve the issue:
//word.clear();
//word.seekg(0);
word >> number;
if(word.fail()) {
cerr << "Failed to read int" << endl;
return 1;
}
cout << in << ' ' << number << endl;
}
As it stands, it always fails on second loop iteration. However, uncommenting any one of the two commented lines of code solves the issue. What I don't get is, since I've reset the stringstream with word.str(in)
, why does it still fail? And why does resetting the get position solve the problem?
Am I missing something about the workings of a stringstream? Does it set the eofbit
flag on the last valid read rather than on the read that fails due to EOF? And if so, why does seekg(0)
seem to clear that flag, while resetting the stringstream doesn't?
The clear() member function is inherited from ios and is used to clear the error state of the stream, e.g. if a file stream has the error state set to eofbit (end-of-file), then calling clear() will set the error state back to goodbit (no error). For clearing the contents of a stringstream , using: m. str("");
A stringstream associates a string object with a stream allowing you to read from the string as if it were a stream (like cin). To use stringstream, we need to include sstream header file. The stringstream class is extremely useful in parsing input.
If you look at the state of the stream, this should be a bit clearer.
int main()
{
std::vector<std::string> words = { "10", "55", "65" };
std::istringstream word;
for (const auto &in : words)
{
word.str(in);
std::cout << "stream state:"
<< (word.rdstate() & std::ios::badbit ? " bad" : "")
<< (word.rdstate() & std::ios::failbit ? " fail" : "")
<< (word.rdstate() & std::ios::eofbit ? " eof" : "")
<< std::endl;
int number;
word >> number;
if (word.fail()) {
std::cerr << "Failed to read int" << std::endl;
return 1;
}
std::cout << in << ' ' << number << std::endl;
std::cout << "stream state:"
<< (word.rdstate() & std::ios::badbit ? " bad" : "")
<< (word.rdstate() & std::ios::failbit ? " fail" : "")
<< (word.rdstate() & std::ios::eofbit ? " eof" : "")
<< std::endl;
}
}
Which will result in:
stream state: 10 10 stream state: eof stream state: eof Failed to read int
So initially none of the flags are set, but reading the number reaches the end of the stream, setting eofbit
. std::istringstream::str
is defined as if to call rdbuf()->str(new_str)
. That says nothing about clearing flags.
Calling clear()
will of course clear the eofbit
, but so does calling seekg
in C++11 or newer. "
Before doing anything else, seekg clears eofbit. (since C++11)".
Note that if you had say "10 20", that it will just discard the " 20", and not detect an error.
stream state: 10 20 10 stream state: stream state: 55 55 stream state: eof stream state: eof Failed to read int
So you might actually want to check that eof flag to see if you read the entire stream.
As noted by others, of course constructing a new stream object each loop also means no concerns over previous flags/states.
As @Someprogrammerdude suggests: simply move your istringstream
inside your while
loop (which you can change to a for
loop to keep in
in the loop as well):
for (string in; cin >> in;)
{
istringstream word(in);
int number;
if (!(word >> number))
{
cerr << "Failed to read int" << endl;
return 1;
}
cout << in << ' ' << number << endl;
}
that way it's re-created each loop.
While you're at it, move number
in there too (unless you use it outside the loop, of course).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With