0

stringstream always seems to fail when I call stringstream::ignore(), even if this is done after calling stringstream::clear():

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cassert>

using namespace std;

int main() {
    int a, b;
    stringstream ss;
    string str;
    ifstream inFile("file.txt");
    if(!inFile) {
        cerr << "Fatal: Cannot open input file." << endl;
        exit(1);
    }

    while(getline(inFile, str)) {
        ss << str;                // read string into ss
        ss >> a >> b;             // stream fails trying to store string into int

        ss.clear();               // reset stream state
        assert(ss.good());        // assertion succeeds

        ss.ignore(INT_MAX, '\n'); // ignore content to next newline
        assert(ss.good());        // assertion fails, why?
    }

    return 0;
}

file.txt contains the following text:

123 abc
456 def

Why is ss.good() false after ss.ignore()?

bwDraco
  • 2,514
  • 2
  • 33
  • 38

2 Answers2

1

std::endl outputs \n and flushes the stream. However, stringstream::flush() is meaningless and does nothing. flush only has meaning when the underlying buffer is tied to an output device like the terminal, however, a stringstream has nowhere to flush the contents to. If you want to clear the contents of a stringstream do ss.str(""); instead. However, I would probably change the code to the following:

while(getline(inFile, str)) {
    ss.str(str);              // call ss.str() to assign a new string to the stringstream
    if(!ss >> a >> b)         // check if stream fails trying to store string into int
    {
        ss.clear();           // Read failed, so reset stream state
    }
    else
    {
        // Read successful
    }
    // Do other stuff
}

Also, if you want to insert a newline into the stringstream, just do ss << '\n'; and do not call std::endl.

Jesse Good
  • 50,901
  • 14
  • 124
  • 166
  • 1
    Last sentence: `std::ends` does not put '\n' but '\0' – PiotrNycz Sep 22 '12 at 13:29
  • A null character could cause unexpected behavior. – bwDraco Sep 22 '12 at 16:35
  • @DragonLord: Yeah, `std::ends` isn't used much anymore (it is leftover from days when `string` and `stringstream` didn't append a null terminator for you). There is a [good question here about it](http://stackoverflow.com/questions/2338377/what-use-is-there-for-ends-these-days). – Jesse Good Sep 22 '12 at 20:48
0

It turns out there is no newline at the end of ss. After executing the following statements:

getline(infile, str);
ss << str;

ss will not contain a newline character, because getline() does not add a newline character to the end of the string stored into the second parameter. As a result, when this statement is executed:

ss.ignore(INT_MAX, '\n');

the stream fails because it reaches the end of the stream without finding a newline character to stop at.


ss.ignore() is not necessary if ss.str() is used to store the string, which replaces the entire contents of the stream. Should the stream fail, it should be reset and its contents set to the empty string "". Alternatively, ss.ignore() could be used, but only as long as a newline character is inserted into the stream immediately after the data is read so that it does not cause the stream to fail—but this would be redundant if the contents of the stream is later set to another value using ss.str().

A successful read of the next line of the file can be ensured by calling ss.clear() before the stream is assigned the contents of the next line of the file, since the old contents of the stream are overwritten on ss.str(). The stream state can be reset at the beginning of the loop, and no problems would occur even if the stream fails later in the loop:

while(getline(inFile, str)) {
    ss.clear();   // make sure stream is good
    ss.str(str);  // overwrite contents of stream with str
    ss >> a >> b;
    // Even if the stream fails after this line, the stream is reset before each
    // line is stored into the stream, and no problems should occur while reading
    // and parsing subsequent lines in the file.

    // Code to validate and store data from file...
}
bwDraco
  • 2,514
  • 2
  • 33
  • 38
  • `flush()` does nothing for `stringstream`, also no need to use `endl` or `ignore`, see my answer. – Jesse Good Sep 22 '12 at 07:29
  • Looks better. Also, instead of calling `ss.clear()` every loop, I would do `if(!ss >> a >> b) ss.clear();` so it is only called when the stream fails (I changed my answer also to reflect this). – Jesse Good Sep 22 '12 at 20:23