相当于C ++的StringBuffer / StringBuilder?
有没有提供有效的string连接function的C ++标准模板库类,类似于C#的StringBuilder或Java的StringBuffer ?
注意这个答案最近得到了一些关注。 我不是主张这是一个解决scheme(这是我在过去STL之前看到的一个解决scheme)。 这是一个有趣的方法,只应用于std::string
或std::stringstream
如果你的代码分析后,你发现这是一个改进。
我通常使用std::string
或std::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::string
或std::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::ostringstream
的operator<<
效率,但更多的尝试,我能够使一个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以及)