How do you clear a stringstream variable?
Asked Answered
I

10

593

I've tried several things already,

std::stringstream m;
m.empty();
m.clear();

both of which don't work.

Inanity answered 21/8, 2008 at 18:35 Comment(0)
L
934

For all the standard library types the member function empty() is a query, not a command, i.e. it means "are you empty?" not "please throw away your contents".

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("");

is correct, although using:

m.str(std::string());

is technically more efficient, because you avoid invoking the std::string constructor that takes const char*. But any compiler these days should be able to generate the same code in both cases - so I would just go with whatever is more readable.

Lemuroid answered 21/8, 2008 at 19:0 Comment(13)
Here is what happens when you forget the "clear()" part. https://mcmap.net/q/65693/-how-to-clear-stringstream-duplicate/635549Haygood
why is it that the m.str() returns the string values but does not clear the stream?Spearing
@KshitijBanerjee I think in C++ m.str() and m.str("") are two different functions. m.str() invokes a function which didn't expect any parameter whereas m.str("") will invoke the function which accepts a const char* parameter. m.str() might have been implemented as a get function which returns the string whereas m.str("") might have been implemented as a set function.Papyrus
Following link neatly documents both versions of str en.cppreference.com/w/cpp/io/basic_stringstream/strHammerless
As galath said it is very important also to add m.clear(); in addition to m.str("");. Otherwise you can get problems if at some point you fill the stringstream with an empty string.Tutor
something weird is happening. After clearing a stringstream with str(""), the stringstream is unable to be set with more data> example: std::stringstream ss; ss << "test"; ss.str(""); ss << "test2"; after this last statement, ss is empty.Brash
@Brash is this really your full context? Your snippet works as expected here: wandbox.org/permlink/PX6ISZbFNpaejfYWHectocotylus
@anthropod Yes that was last year. I have since gotten it working.Brash
Oh boy is this intuitive. Just like everything else about C++!Turbo
Does m.str(std::string()); throw?Papistry
You might want to add a comment that it's completely un-intuitive that m.str("foo"); m.str << "bar" does not return "foobar"Inappreciative
This is the worst accepted AND upvoted answer in Stackoverflow i came across so far. In my std library implementation the stringstream was unusable after this reset. Just use m=std::stringstream(); from the answer of jerron.Lillylillywhite
The same here, on VS 2022 with C++ 20 is indeed m.str() == "" but the stream outputs garbage when reused. The solutions from Nikos Athanasiou, jerron and TimoK work perfectly.Draughts
C
74

You can clear the error state and empty the stringstream all in one line

std::stringstream().swap(m); // swap m with a default constructed stringstream

This effectively resets m to a default constructed state, meaning that it actually deletes the buffers allocated by the string stream and resets the error state. Here's an experimental proof:

int main ()
{
    std::string payload(16, 'x');
    
    std::stringstream *ss = new std::stringstream; // Create a memory leak
    (*ss) << payload;                              // Leak more memory
    
    // Now choose a way to "clear" a string stream
    //std::stringstream().swap(*ss); // Method 1
    //ss->str(std::string());        // Method 2
    
    std::cout << "end" << std::endl;
}

Demo

When the demo is compiled with address sanitizer, memory usage is revealed:

=================================================================
==10415==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 392 byte(s) in 1 object(s) allocated from:
    #0 0x510ae8 in operator new(unsigned long) (/tmp/1637178326.0089633/a.out+0x510ae8)
    #1 0x514e80 in main (/tmp/1637178326.0089633/a.out+0x514e80)
    #2 0x7f3079ffb82f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291

Indirect leak of 513 byte(s) in 1 object(s) allocated from:
    #0 0x510ae8 in operator new(unsigned long) (/tmp/1637178326.0089633/a.out+0x510ae8)
    #1 0x7f307b03a25c in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::reserve(unsigned long) (/usr/local/lib64/libstdc++.so.6+0x13725c)
    #2 0x603000000010  (<unknown module>)

SUMMARY: AddressSanitizer: 905 byte(s) leaked in 2 allocation(s).

Pretty steep if you ask me. To hold just 16bytes of payload, we spent 905 bytes ... string streams are no toy. Memory is allocated in two parts:

  • The constructed string stream (392 bytes)
  • The extra buffer needed for the payload (513 bytes). The extraneous size has to do with the allocation strategy chosen by the stream and for payloads <= 8 bytes, blocks inside the initial object can be used.

If you enable method 1 (the one shown in this answer) the extra 513 (payload) bytes are reclaimed, because the stream is actually cleared.

If you enable method2 as suggested in the comments or other answers, you can see that all 905 bytes are in use by the time we exit.

In terms of program semantics, one may only care that the stream "appears" and "behaves" as empty, similar to how a vector::clear may leave the capacity untouched but render the vector empty to the user (of course vector would spend just 16 bytes here). Given the memory allocation that string stream requires, I can imagine this approach being often faster. This answer's primary goal is to actually clear the string stream, given that memory consumption that comes with it is no joke. Depending on your use case (number of streams, data they hold, frequency of clearing) you may choose the best approach.

Finally note that it's rarely useful to clear the stream without clearing the error state and all inherited state. The one liner in this answer does both.

Casper answered 24/4, 2014 at 10:34 Comment(16)
This is the most efficient and most elegant way to do it compared to all other answers here. However, std::stringstream::swap is a c++11 feature and this solution doesn't work for prior c++11 compilers.Layout
Feature still missing in GNU g++ v4.8, see #24429941Befog
@101010: How is swapping better than move-assignment?Madonna
@Madonna - swap is noexceptGraminivorous
@AsetD: Even if it is noexcept, did you forget the default-constructed temporary?Madonna
This is low effecient. When I want to re use original ss. It swaps an empty for me.Anoa
@Layout I would expect this to be much less efficient that calling m.str({}), as Zhang's comment said. To clarify: When you call m.str({}) I would expect that it would reuse some of the memory it allocated during the first operation to speed up the next operation. When you call std::stringstream().swap(m) I expect any memory it allocated during the first operation would have its ownership transferred to the temporary you just created, losing the chance for that efficiency. It would interesting to see a comparison in practice though.Mississippian
might as well just do m = std::stringstream(); rather than swapping with an object you're deletingPyrargyrite
I think this causes memory leak as the original m 's memory address will be left in the memory without destruction. The stringstream() construct creates a new object and swaps it with m.Theadora
I think swap is a good general solution because streams can have a lot of internal state. Regarding temporaries, I would expect the compiler to eliminate them. And allocation is fraught; eschew pre-optimization. I would expect allocation to typically be pointer fiddling.Galleon
@shaheeng That is wrong. The old m gets swapped into the unnamed temporary, which is destructed at the end of the statement and thus frees all resources previously owned by m. Or if you meant the assignment from a default-constructed instance, then the assignment operator is specified not to leak (because why would it?). Nothing is "left in the memory without destruction". C++ with its RAII ensures things are destroyed automatically when appropriate, unless you go out of your way to allocate manually and don't free, but you should just not do that.Wailoo
I believe that in my system, this statement executed more slowly than the m.str(std::string()) version. However, I left out the m.clear(), so that could also be a contributorShanna
@Shanna There's more things involved in choosing a proper method. Probably that's why the standard does not provide a reset function (yet). I've updated my answer to hint at more factors that may affect your choice. As stated among others it's hardly useful to clear the stream without clearing the error state. Then there's the memory consideration.Casper
But isn't the whole point of clearing a stringstream avoiding the construction of a new one?Patronizing
@Patronizing If you opt for m.str(""), just remember to clear the state, this may happen if you don't (when I say state, I mean the error state and all inherited state - I couldn't guarantee those two are one and the same). I don't have extra remarks on the "whole point", all I (re)searched is shown in the answer.Casper
@NikosAthanasiou: Yes, I've been burned by this recently. I don't even like istringstreams, it's just that formatted input is such a mess in the standard library... a bunch of different partial facilities with overlaps and different paradigms...Patronizing
U
46

This should be the most reliable way regardless of the compiler:

m=std::stringstream();
Unimpeachable answered 11/10, 2012 at 16:0 Comment(9)
This is better in my opinion because m.str(""); caused my stringstream to be stuck with that empty value whatever I tried. But using this I don't have that problemBonine
I ran into the same problem, for me mm.clear(); mm.str(""); did the trick. (no C++11, else swap would be better).Griddle
@hochl: Why would swap be better than move-assignment?Madonna
I guess that would be ok as well. If performance is important you may want to check which is faster and choose accordingly.Griddle
Almost certainly the fastest and best answer but requires C++11.Laryngotomy
It's not good for all situation. This would re-allocate the buffer every time while mm.str("") would not.Exegete
My primary use-case for flushing a stringstream object is keeping a threadlocal stringstream object around to prevent unecessary instantiation of the stringstream -- instantiating a new stringstream object copies the global locale object -- theoretically this is quick and only involved incrementing an atomic, but at the level of concurrency I deal with it's often crippling.Syst
use of deleted function ‘std::basic_stringstream<char>& std::basic_stringstream<char>::operator=(const std::basic_stringstream<char>&)Esther
@ShitalShah Reallocating the buffer is not generally a bad thing. For example, if the usual buffer size used is 200 Bytes, but sometimes, it is 20 kB, then one would keep around those 20 kB from the max buffer ever used.Kingery
I
39
m.str("");

seems to work.

Inanity answered 21/8, 2008 at 18:36 Comment(1)
That would be cleared by .clear() which is specified in the OP.Elum
S
18

I am always scoping it:

{
    std::stringstream ss;
    ss << "what";
}

{
    std::stringstream ss;
    ss << "the";
}

{
    std::stringstream ss;
    ss << "heck";
}
Sydelle answered 26/3, 2014 at 17:37 Comment(1)
Is this better than clearing the stringstream? Tropine
R
14

my 2 cents:

this seemed to work for me in xcode and dev-c++, I had a program in the form of a menu that if executed iteratively as per the request of a user will fill up a stringstream variable which would work ok the first time the code would run but would not clear the stringstream the next time the user will run the same code. but the two lines of code below finally cleared up the stringstream variable everytime before filling up the string variable. (2 hours of trial and error and google searches), btw, using each line on their own would not do the trick.

//clear the stringstream variable

sstm.str("");
sstm.clear();

//fill up the streamstream variable
sstm << "crap" << "morecrap";
Rosenblatt answered 18/10, 2013 at 0:34 Comment(0)
U
4

There are many other answers that "work", but they often do unnecessary copies or reallocate memory.

  1. Swapping streams means that you need to discard one of them, wasting the memory allocation. Same goes for assigning a default-constructed stream,

  2. Assigning to the string in the string buffer (via stringstream::str or stringbuf::str) may lose the buffer already allocated by the string.

The canonical way to clear the string stream would be:

void clear(std::stringstream &stream)
{
   if (stream.rdbuf()) stream.rdbuf()->pubseekpos(0);
}

The canonical way to get the size of the data in the stream's buffer is:

std::size_t availSize() (const std::stringstream& stream)
{
   if (stream.rdbuf())
      return std::size_t(
         stream.rdbuf()->pubseekoff(0, std::ios_base::cur, std::ios_base::out));
   else
      return 0;
}

The canonical way to copy the data from the stream to some other preallocated buffer and then clear it would then be:

std::size_t readAndClear(std::stringstream &stream, void* outBuf, std::size_t outSize)
{
   auto const copySize = std::min(availSize(stream), outSize);
   if (!copySize) return 0; // takes care of null stream.rdbuf()

   stream.rdbuf()->sgetn(outBuf, copySize);
   stream.rdbuf()->pubseekpos(0); // clear the buffer

   return copySize;
}

I intend this to be a canonical answer. Language lawyers, feel free to pitch in.

Upraise answered 21/8, 2008 at 18:36 Comment(1)
I upvoted this answer but then discovered that it does not actually set current size to zero, so the old content still visible: godbolt.org/z/erP8dMcfKBarfly
H
1

@Azeem's answer should be considered the best solution here. On the one hand his answer doesn't address the specific words of the OP's question, but on the other hand I think it addresses the intent of the OP's question. Excerpting @Azeem's code:

{
    std::stringstream ss;
    ss << "what";
}

Benefits of using scoping contexts:

  • Reusing a variable is a code smell, so you should avoid doing it whenever possible. https://maximilianocontieri.com/code-smell-107-variables-reuse
  • When the context scope is entered you're guaranteed to have a new instance that's correctly initialized.
  • When the context scope is exited the instance is guaranteed to be destroyed correctly and all the memory is reclaimed.
  • It doesn't require knowing how a stringstream is implemented (buffer, pointer, index, etc). If you find that you need to know how a class is implemented, then either the class interface has been poorly designed, or you're just using the class wrong -- and that makes your code poorly designed.
  • It relies on the stringstream's constructor and destructor to do the right thing (code that's been thoroughly tested and debugged) instead of relying on code that you just wrote to do the right thing.
  • It's easy to type, easy to read, easy to understand: just two braces for each context.

Drawbacks:

  • You have to learn about contexts / blocks / scopes. https://en.cppreference.com/w/cpp/language/scope. There, you're finished.
  • It seems like it would require extra CPU cycles in order to create a new stringstream and then destroy it once for each context. That's fine, though. Your whole program uses extra CPU cycles. Don't count cycles (or even think about them) until you've proven empirically that you're running out of them.
Haphtarah answered 28/10, 2023 at 9:54 Comment(1)
You are right. Restrict the scope of each variable and never reuse a variable related to a string, because strings are inherently immutable.Draughts
S
-1

It's a conceptual problem.

Stringstream is a stream, so its iterators are forward, cannot return. In an output stringstream, you need a flush() to reinitialize it, as in any other output stream.

Sturgill answered 10/10, 2014 at 20:4 Comment(1)
en.cppreference.com/w/cpp/io/basic_ostream/flush flush synchronizes with the storage device it's associated with. This is not the same reinitialization.Trogon
F
-14

These do not discard the data in the stringstream in gnu c++

    m.str("");
    m.str() = "";
    m.str(std::string());

The following does empty the stringstream for me:

    m.str().clear();
Fullrigged answered 6/9, 2012 at 22:58 Comment(3)
I'm not so sure this would work, because of the same reasons bernhardrusch's wouldn't work. The .str() function returns a copy, and clearing the copy wouldn't do anything.Giovannagiovanni
This solution does NOT work for Microsoft Visual C++.Mercurous
Incorrect. Clear would be operating on the string returned from the stream, not the stream itself.Morula

© 2022 - 2024 — McMap. All rights reserved.