相当于C ++的StringBuffer / StringBuilder?

有没有提供有效的string连接function的C ++标准模板库类,类似于C#的StringBuilder或Java的StringBuffer ?

注意这个答案最近得到了一些关注。 我不是主张这是一个解决scheme(这是我在过去STL之前看到的一个解决scheme)。 这是一个有趣的方法,只应用于std::stringstd::stringstream如果你的代码分析后,你发现这是一个改进。

我通常使用std::stringstd::stringstream 。 我从来没有遇到过这些问题。 如果我事先知道string的大小,通常我会预留一些空间。

我见过其他人在遥远的过去使自己优化的string生成器。

 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; } }; 

它使用两个string作为大部分string,另一个作为连接短string的划痕区域。 它通过将一个小string中的短附加操作进行批处理,然后将其附加到主string中来优化附加操作,从而减less主string所需的重新分配次数。

我没有要求与std::stringstd::stringstream这个技巧。 我认为这是用std :: string之前的第三方string库,这是很久以前。 如果您采用这样的策略,则首先应用您的应用程序。

C ++的方式是使用std :: stringstream或只是纯string连接。 C ++string是可变的,所以连接的性能考虑就不那么重要了。

关于格式化,您可以在一个stream上执行所有相同的格式,但以一种不同的方式,类似于cout 。 或者你可以使用一个强types的函子来封装这个函数,并提供一个类似于String.Format的接口,例如boost :: format

std :: string.append函数不是一个好的select,因为它不接受许多forms的数据。 更有用的替代方法是使用std:stringstream,如下所示:

 #include <sstream> // ... std::stringstream ss; //put arbitrary formatted data into the stream ss << 4.5 << ", " << 4 << " whatever"; //convert the stream buffer into a string std::string str = ss.str(); 

std::string C ++的等价物:它是可变的。

您可以使用.append()来简单地连接string。

 std::string s = "string1"; s.append("string2"); 

我想你甚至可以做到:

 std::string s = "string1"; s += "string2"; 

至于C#的StringBuilder的格式化操作,我相信snprintf (或sprintf如果你想冒险编写错误的代码;-))到字符数组并转换回string是唯一的select。

因为C ++中的std::string是可变的,所以可以使用它。 它有一个+= operator和一个append函数。

如果您需要附加数字数据,请使用std::to_string函数。

如果你想要能够将任何对象序列化成string的forms更灵活,那么使用std::stringstream类。 但是,您需要实现自己的stream式运算符function,以便与您自己的自定义类一起工作。

std :: string的+ =不能和const char *一起工作(像“要添加的string”这样的东西似乎是),所以绝对使用stringstream是最接近需要的 – 你只需要使用<<而不是+

如果必须将string插入/删除到目标string的随机位置或长字符序列,则绳索容器可能是值得的。 这是SGI实施的一个例子:

 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::ostringstreamoperator<<

效率,但更多的尝试,我能够使一个StringBuilder在某些情况下更快。

每次我追加一个string,我只是在某处存储引用,并增加总大小的计数器。

我最终实现它的真正方法(恐怖!)是使用不透明的缓冲区(std :: vector <char>):

  • 1个字节的标题(告诉2个数据是:移动string,string还是字节[])
  • 6位来告诉byte []的长度

对于byte []

  • 我直接存储短string的字节(用于顺序存储器访问)

对于移动的string (string附加了std::move

  • 指向std::string对象的指针(我们拥有所有权)
  • 如果在那里有未使用的保留字节,在类中设置一个标志

为string

  • 指向std::string对象的指针(没有所有权)

还有一个小的优化,如果最后一个插入的string被移入,它检查空闲的保留但未使用的字节,并在那里存储更多的字节,而不是使用不透明的缓冲区(这是为了节省一些内存,它实际上使它稍慢,也许也取决于CPU,并且很less看到带有额外保留空间的string)

这比std::ostringstream稍微快一点,但是它有一些缺点:

  • 我假设固定长度的字符types(所以1,2或4字节,对UTF8不好),我不是说它不适用于UTF8,只是我没有检查它的懒惰。
  • 我用错误的编码做法(不透明的缓冲区,容易使其不便携带,我相信我的便携式)
  • 缺乏ostringstream所有function
  • 如果某个引用的string在Mergin所有string之前被删除:未定义的行为。

结论? 使用std::ostringstream

它已经解决了最大的瓶颈问题,而在实施速度方面只有几个百分点的速度并不值得。

一个方便的C ++string生成器

就像很多人之前回答的一样,std :: stringstream是select的方法。 它工作良好,并有很多转换和格式化选项。 国际海事组织它有一个相当不方便的缺陷,但不能用它作为一个class轮或作为一个expression。 你总是要写:

 std::stringstream ss; ss << "my data " << 42; std::string myString( ss.str() ); 

这是相当恼人的,尤其是当你想在构造函数中初始化string。

原因是,a)std :: stringstream没有转换运算符到std :: string和b)stringstream的运算符<<()没有返回一个stringstream引用,而是一个std :: ostream引用 – 不能进一步计算为stringstream。

解决的办法是重写std :: stringstream并给它更好的匹配运算符:

 namespace NsStringBuilder { template<typename T> class basic_stringstream : public std::basic_stringstream<T> { public: basic_stringstream() {} operator const std::basic_string<T> () const { return std::basic_stringstream<T>::str(); } basic_stringstream<T>& operator<< (bool _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (char _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (signed char _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (unsigned char _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (short _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (unsigned short _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (int _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (unsigned int _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (long _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (unsigned long _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (long long _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (unsigned long long _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (float _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (double _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (long double _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (void* _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (std::streambuf* _val) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (std::ostream& (*_val)(std::ostream&)) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (std::ios& (*_val)(std::ios&)) { std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (std::ios_base& (*_val)(std::ios_base&)){ std::basic_stringstream<T>::operator << (_val); return *this; } basic_stringstream<T>& operator<< (const T* _val) { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val)); } basic_stringstream<T>& operator<< (const std::basic_string<T>& _val) { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val.c_str())); } }; typedef basic_stringstream<char> stringstream; typedef basic_stringstream<wchar_t> wstringstream; } 

有了这个,你可以写下类似的东西

 std::string myString( NsStringBuilder::stringstream() << "my data " << 42 ) 

即使在构造函数中。

我不得不承认我没有衡量性能,因为我还没有在一个大量使用string的环境中使用它,但是我认为它不会比std :: stringstream差太多,因为一切都完成了通过引用(除了转换为string,但多数民众赞成复制操作在std :: stringstream以及)