Is there a C++ Standard Template Library class that provides efficient string concatenation functionality, similar to C#\'s StringBuilder or Java\'s StringBuffer?
The C++ way would be to use std::stringstream or just plain string concatenations. C++ strings are mutable so the performance considerations of concatenation are less of a concern.
with regards to formatting, you can do all the same formatting on a stream, but in a different way, similar to cout. or you can use a strongly typed functor which encapsulates this and provides a String.Format like interface e.g. boost::format
NOTE this answer has received some attention recently. I am not advocating this as a solution (it is a solution I have seen in the past, before the STL). It is an interesting approach and should only be applied over std::string
or std::stringstream
if after profiling your code you discover this makes an improvement.
I normally use either std::string or std::stringstream. I have never had any problems with these. I would normally reserve some room first if I know the rough size of the string in advance.
I have seen other people make their own optimized string builder in the distant past.
class StringBuilder {
private:
std::string main;
std::string scratch;
const std::string::size_type ScratchSize = 1024; // or some other arbitrary number
public:
StringBuilder & append(const std::string & str) {
scratch.append(str);
if (scratch.size() > ScratchSize) {
main.append(scratch);
scratch.resize(0);
}
return *this;
}
const std::string & str() {
if (scratch.size() > 0) {
main.append(scratch);
scratch.resize(0);
}
return main;
}
};
It uses two strings one for the majority of the string and the other as a scratch area for concatenating short strings. It optimise's appends by batching the short append operations in one small string then appending this to the main string, thus reducing the number of reallocations required on the main string as it gets larger.
I have not required this trick with std::string
or std::stringstream
. I think it was used with a third party string library before std::string, it was that long ago. If you adopt a strategy like this profile your application first.
std::string's += doesn't work with const char* (what stuff like "string to add" appear to be), so definitely using stringstream is the closest to what is required - you just use << instead of +
I wanted to add something new because of the following:
At a first attemp I failed to beat
std::ostringstream
's operator<<
efficiency, but with more attemps I was able to make a StringBuilder that is faster in some cases.
Everytime I append a string I just store a reference to it somewhere and increase the counter of the total size.
The real way I finally implemented it (Horror!) is to use a opaque buffer(std::vector < char > ):
for byte [ ]
for moved strings (strings appended with std::move
)
std::string
object (we have ownership)for strings
std::string
object (no ownership)There's also one small optimization, if last inserted string was mov'd in, it checks for free reserved but unused bytes and store further bytes in there instead of using the opaque buffer (this is to save some memory, it actually make it slightly slower, maybe depend also on the CPU, and it is rare to see strings with extra reserved space anyway)
This was finally slightly faster than std::ostringstream
but it has few downsides:
ostringstream
conclusion? use
std::ostringstream
It already fix the biggest bottleneck while ganing few % points in speed with mine implementation is not worth the downsides.
The Rope container may be worth if have to insert/delete string into the random place of destination string or for a long char sequences. Here is an example from SGI's implementation:
crope r(1000000, 'x'); // crope is rope<char>. wrope is rope<wchar_t>
// Builds a rope containing a million 'x's.
// Takes much less than a MB, since the
// different pieces are shared.
crope r2 = r + "abc" + r; // concatenation; takes on the order of 100s
// of machine instructions; fast
crope r3 = r2.substr(1000000, 3); // yields "abc"; fast.
crope r4 = r2.substr(1000000, 1000000); // also fast.
reverse(r2.mutable_begin(), r2.mutable_end());
// correct, but slow; may take a
// minute or more.
std::string
is the C++ equivalent: It's mutable.