how to reuse stringstream
Asked Answered
H

7

23

These threads do NOT answer me:

resetting a stringstream

How do you clear a stringstream variable?

std::ifstream file( szFIleName_p );
if( !file ) return false;

// create a string stream for parsing

std::stringstream szBuffer;

std::string szLine;     // current line
std::string szKeyWord;  // first word on the line identifying what data it contains

while( !file.eof()){

    // read line by line

    std::getline(file, szLine);

    // ignore empty lines

    if(szLine == "") continue;

    szBuffer.str("");
    szBuffer.str(szLine);
    szBuffer>>szKeyWord;

szKeyword will always contain the first word, szBuffer is not being reset. I can't find a clear example anywhere on how to use stringstream.

New code after answer:

...
szBuffer.str(szLine);
szBuffer.clear();
szBuffer>>szKeyWord;
...

Ok, thats my final version:

std::string szLine;     // current line
std::string szKeyWord;  // first word on the line identifying what data it contains

// read line by line

while( std::getline(file, szLine) ){

    // ignore empty lines

    if(szLine == "") continue;

    // create a string stream for parsing

    std::istringstream szBuffer(szLine);
    szBuffer>>szKeyWord;
Houseyhousey answered 24/8, 2012 at 15:21 Comment(1)
possible duplicate of How to reuse an ostringstream?Junior
S
36

You didn't clear() the stream after calling str(""). Take another look at this answer, it also explains why you should reset using str(std::string()). And in your case, you could also reset the contents using only str(szLine).

If you don't call clear(), the flags of the stream (like eof) wont be reset, resulting in surprising behaviour ;)

Schleiermacher answered 24/8, 2012 at 15:28 Comment(6)
dammit, I saw a thread saying clear just clears error flags..wtf man..can you explain me why I need both ? using just clear seems to work..?Houseyhousey
These are related. If you don't call clear(), the stream will think its still in the eof state.Schleiermacher
So clear important task on reset is putting the seek pointer (whatever it is called) at the beginning again? In the end, thats what I need: szBuffer.str(szLine);szBuffer.clear();Houseyhousey
Its not about the seek pointer, its about the whole state of the stream. And yes, in your case you can simply "overwrite" the previous content like that.Schleiermacher
Any chance youd be able to explain how not calling clear make it outputs a string from the previous content? I mean, that monster is holding all the strings allways?Houseyhousey
This doesn't actually work reliably. You also need to reset any formatting flags to the default. Including those you don't know about, because they were allocated by a call to std::ios_base::xalloc() behind your back.Dronski
D
11

It depends what you're doing with it. It's generally easier to just create a new istringstream or ostringstream. To "reset" a stream, you have to clear its buffer, clear any error flags, reset any formatting flags, plus the precision and fill, reimbue it with the original locale, and not forget any extended formatting information generated with a value returned from xalloc. In sum, impossible to get correct.

And while I'm at it, your loop is wrong, and will probably result in the last line being processed twice. file.eof() only has a usable meaning after the input has failed (and even then, it's not 100% reliable). What you want is:

std::string line;
while ( std::getline( file, line ) ) {
    if ( !line.empty() ) {
        std::istringstream buffer( line );
        //  ...
    }
}

(Actually, you probably want to trim trailing white space from the line before the test for empty.)

Dronski answered 24/8, 2012 at 15:46 Comment(5)
I love how there's a lot of resources out there explaining all those very obvious std information out there, in an organized and well documented way, notHouseyhousey
I could swear there was a committee member in a video claiming people were getting bad performance with streams because they were using them incorrectly by recreating them in loops instead of reusing references to them. I feel my life is a lie.Barkentine
How about moving the buffer out of the old stringstream, then creating a fresh stringstream, using the old buffer? That way, you would reset everything, but reuse the storage. I tentatively think the methods added in C++20 allow this.Zeist
Having spent a few minutes playing, this seems to work: std::stringstream ss; /* use ss */ std::string buffer = std::move(ss.rdbuf()->str()); buffer.resize(0); ss = std::stringstream(std::move(buffer));? Not sure if this actually reuses the allocation, though!Zeist
@arthropod: Parsing code that's focused on performance definitely should not create stringstreams in a loop. Parsing code that's really focused on performance shouldn't create any stringstreams outside the loop either! These statements remain true if you substitute "performance" with "consistency," "correctness," or "testability".Albany
H
3

ss is stringstream. Use

  • first step: ss.clear();
  • second step: ss.str("");

to reuse the stringstream -- completely clear the string stream.

Helman answered 30/12, 2015 at 23:9 Comment(0)
E
1

In most cases, it is easier to create a new istringstream or ostringstream instead of resetting the same ones.

However, if you do want to resent them:

  • Resent flags of the stream (to avoid unexpected behavior) using clear ().

  • While you can correct the contents of your stringstream using str (""), for efficiency purposes we might prefer str(std::string()).

Enrico answered 3/1, 2016 at 19:13 Comment(0)
I
0

If you have the stream in a class member, use unique_ptr<stringstream>, then just reset(new stringstream(...)) to reuse it.

Ideogram answered 23/1, 2017 at 16:1 Comment(0)
J
0

Imagine a config file.

par1=11
par2=22

codes:

std::string line, strpar1, strpar2;
int par1, par2;
std::ifstream configfile("config.cfg");

std::getline(configfile, line);    // first line to variable "line"
std::istringstream sline(line);
while (std::getline(sline, strpar1, '='));
par1 = std::stoi(strpar1);  // par1 get 11

bool b = sline.eof(); // true

std::getline(configfile, line);    // second line to variable "line"
sline.clear();    //
sline.str(line);    // reuse "sline"

b = sline.good();  // true  // goodbit is zero, indicating that none of the other bits is set.
b = sine.fail();  // false
b = sline.bad();  // false
b = sline.eof(); // false

while (std::getline(sline, strpar2, '='));
par2 = std::stoi(strpar2);  // par2 get 22

goodbit is zero, indicating that none of the other bits is set

Junction answered 20/6, 2018 at 6:30 Comment(0)
D
0

To reuse stringstream you need to reset the flags first and set the internal stringbuf ptr to the beginning by

std::stringstream ss;
...
ss.clear();
ss.seekp(0, std::ios_base::beg);

This will print correct values after each str() call.

Note, clear() wont reset used formatting flags and each str() call will copy out a new string. So reusing the stringstream has few to no clock cycle advantages. For a application which uses thread specific (thread_local) stringstream, behavior is waste of resources.

Delainedelainey answered 4/5, 2024 at 14:33 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.