0

I'm basically trying to add integers to a string, and then add endline chars to the string at specific times, and then continue adding integers. I want it all added to just one string object.

So I added an int to stringstream, fed stringstream to a string. Then added '\n' to the string.

Then I added another int to stringstream, and fed it to the string again.

Unfortunately, the second object from the stringstream object is not being added to the string, and I'm not sure why it cannot.

I tried adding two objects to my string stream object, and then feeding those to the string one at a time (with the endline character in between), but stringstream adds everything in the string to the string at once, so that doesn't work.

int main(){
    string h;
    stringstream ss;

    ss << 1;
    ss >> h;
    h += '\n';
    ss << 2;
    ss >> h;
    h += '\n';
    cout << h << "endline";
    cout << endl;

    return 0;
}

There are no error messages of course. I expect it to output:

1
2
endline

Instead I get

1

endline

So obviously the string is adding both of my endline characters, but not the 2 that I added to the stringstream.

r8__.
  • 3
  • 2
  • Always test IO for success. Always. Side note: `ss >> h;` will, if it succeeds, write over everything already in `h`. If it doesn't succeed, you probably shouldn't use `h`. – user4581301 Oct 30 '19 at 05:37
  • Thank you! I did not know that it would overwrite everything in the string. What I ended up doing was `ss >> tempString` then `h+= tempString` then `ss = stringstream()`. And then repeating as necessary. – r8__. Oct 30 '19 at 05:39
  • 1
    @r8__. You can reset the stringstream after each `>>` with `ss.str(""); ss.clear();` Then you are reusing the existing stringstream without creating a new one. – jignatius Oct 30 '19 at 06:05
  • Gotcha. Thanks! – r8__. Oct 30 '19 at 06:49
  • 1
    @r8__. You can use `std::to_string()` instead of `std::stringstream`, eg: `string h; h += to_string(1); h += '\n'; h += to_string(2); h += '\n';` Or, use `std::ostringstream` for all of the output, then save it to a `std::string` when finished, eg: `ostringstream oss; oss << 1; oss << '\n'; oss << 2; oss << '\n'; string h = oss.str();` – Remy Lebeau Oct 30 '19 at 08:15
  • @RemyLebeau So is the `to_string(int object)` function conversion method quite a bit more efficient than using stringstream to do it? Obviously it has fewer lines of code which is a plus. – r8__. Oct 30 '19 at 15:08
  • @r8__. You will have to look at your compiler's actual implementation of `to_string()` to determine its efficiency over `stringstream`. – Remy Lebeau Oct 30 '19 at 16:36

2 Answers2

6

To demonstrate what happened we need to inspect the various stream status bits.

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main()
{
    string h;
    stringstream ss;

    if (ss << 1)
    {
        cout << "1 success. Fail " << ss.fail() 
             << " eof " << ss.eof() << " bad " << ss.bad() << endl;
        if (ss >> h)
        {
            cout << "2 success. Fail " << ss.fail() 
                 << " eof " << ss.eof() << " bad " << ss.bad() << endl;
            h += '\n';
            if (ss << 2)
            {
                cout << "3 success. Fail " << ss.fail() 
                     << " eof " << ss.eof() << " bad " << ss.bad() << endl;
                if (ss >> h)
                {
                    cout << "4 success. Fail " << ss.fail() 
                         << " eof " << ss.eof() << " bad " << ss.bad() << endl;
                    h += '\n';
                    cout << h << "endline";
                    cout << endl;
                }
                else
                {
                    cout << "4 Fail " << ss.fail() << " eof " << ss.eof() 
                         << " bad " << ss.bad() << endl;
                }
            }
            else
            {
                cout << "3 Fail " << ss.fail() << " eof " << ss.eof() 
                     << " bad " << ss.bad() << endl;
            }
        }
        else
        {
            cout << "2 Fail " << ss.fail() << " eof " << ss.eof() 
                 << " bad " << ss.bad() << endl;
        }
    }
    else
    {
        cout << "1 Fail " << ss.fail() << " eof " << ss.eof() 
             << " bad " << ss.bad() << endl;
    }
    return 0;
}

Output

1 success. Fail 0 eof 0 bad 0
2 success. Fail 0 eof 1 bad 0
3 Fail 1 eof 1 bad 0

So the first write succeeded. Flawless victory. But the first read read everything in the stream and hit the end of file (not that the stream is a file, but the name stuck), setting the EOF bit. Once the EOF bit is set, there isn't much you can do with a stream other than clear the bit and pray someone adds more data to be read.

More data was added to the stream, but the file could not accept it because of the EOF bit.

If we clear the EOF

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main()
{
    string h;
    stringstream ss;

    if (ss << 1)
    {
        cout << "1 success. Fail " << ss.fail() << " eof " << ss.eof() 
             << " bad " << ss.bad() << endl;
        if (ss >> h)
        {
            cout << "2 success. Fail " << ss.fail() << " eof " << ss.eof() 
                 << " bad " << ss.bad() << endl;
            h += '\n';
            ss.clear(); // added this
            if (ss << 2)
            {
                cout << "3 success. Fail " << ss.fail() << " eof " << ss.eof() 
                     << " bad " << ss.bad() << endl;
                if (ss >> h)
                {
                    cout << "4 success. Fail " << ss.fail() << " eof " << ss.eof() 
                         << " bad " << ss.bad() << endl;
                    h += '\n';
                    cout << h << "endline";
                    cout << endl;
                }
                else
                {
                    cout << "4 Fail " << ss.fail() << " eof " << ss.eof() 
                         << " bad " << ss.bad() << endl;
                }
            }
            else
            {
                cout << "3 Fail " << ss.fail() << " eof " << ss.eof() 
                     << " bad " << ss.bad() << endl;
            }
        }
        else
        {
            cout << "2 Fail " << ss.fail() << " eof " << ss.eof() 
                 << " bad " << ss.bad() << endl;
        }
    }
    else
    {
        cout << "1 Fail " << ss.fail() << " eof " << ss.eof() 
             << " bad " << ss.bad() << endl;
    }
    return 0;
}

the output is now

1 success. Fail 0 eof 0 bad 0
2 success. Fail 0 eof 1 bad 0
3 success. Fail 0 eof 0 bad 0
4 success. Fail 0 eof 1 bad 0
2
endline

If we ignore all the status information I added we really got

2
endline

not the desired

1
2
endline

because ss >> h will overwrite everything already in h. The "1\n" is wiped out by the "2"

The easiest way to get what you want is to write everything in and then get the contents as a string.

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main()
{
    string h;
    stringstream ss;

    if (ss << 1 << '\n' << 2 << '\n')
    {
        cout << ss.str() << "endline";
        cout << endl;
    }
    else
    {
        cout << "Fail " << ss.fail() << " eof " << ss.eof()
             << " bad " << ss.bad() << endl;
    }
    return 0;
}

This time the output is

1
2
endline
user4581301
  • 29,019
  • 5
  • 26
  • 45
  • Thank you for the comprehensive answer. I didn't think to include the `'\n'` char into the stringstream. Just due to the nature of my project, `ss.clear()` ended up being my saving grace rather than feeding the string stream with the very long string that I'm making. Thank you for explaining the `eof()` character. That has really helped me to understand stringstream better. – r8__. Oct 30 '19 at 06:54
  • @r8__. a side note: give [Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons). It has nothing to do with your current problem, but it is by far the most common stream reading error because it seems so logical until you pick it apart. If you know the trap ahead of time, it will probably save you some future debugging. – user4581301 Oct 30 '19 at 07:00
3

Use the stringstream to buildup the output you want, and then extract the string.

ss << 1 << '\n';
ss << 2 << '\n';
h = ss.str();
jxh
  • 64,506
  • 7
  • 96
  • 165