是否有一个 C++ 标准模板库类可以提供高效的字符串连接功能,类似于 C# 的 字符串生成器 或Java的 字符串缓冲区?

有帮助吗?

解决方案

注意这个答案最近受到了一些关注。我并不提倡将此作为解决方案(这是我过去在 STL 之前见过的解决方案)。这是一个有趣的方法,应该只应用于 std::string 或者 std::stringstream 如果在对代码进行分析后您发现这有所改进。

我通常使用 std::string 或者 std::stringstream. 。我从来没有遇到过这些问题。如果我提前知道绳子的粗略尺寸,我通常会先保留一些房间。

我在很久以前就见过其他人制作自己的优化字符串生成器。

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

它使用两个字符串,一个用于字符串的大部分,另一个作为用于连接短字符串的暂存区域。它通过在一个小字符串中批处理短追加操作然后将其追加到主字符串来优化追加,从而减少主字符串变大时所需的重新分配次数。

我不需要这个技巧 std::string 或者 std::stringstream. 。我认为它是在 std::string 之前与第三方字符串库一起使用的,那是很久以前的事了。如果您采用这样的策略,首先要分析您的应用程序。

其他提示

在C ++方法是使用的std :: stringstream的或只是简单的字符串连接。所以级联的性能考虑较少关注的C ++字符串是可变的。

至于格式,你可以做一个流中的所有相同的格式,但在以不同的方式,类似于cout 。或者你可以使用强类型的仿函数,它封装这一点,并提供一样的界面,例如一个的String.Format 升压::格式

在的std :: string.append功能是不是一个好的选择,因为它不接受数据的多种形式。一个更有用的方法是使用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()。

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

我想你甚至可以做到:

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

对于C#的StringBuilder的格式化操作,我相信snprintf(或sprintf如果你想冒险写bug的代码;-))到一个字符数组转换回字符串是他们唯一的选择。

由于std::string在C ++中是可变的则可以使用。它有一个+= operatorappend功能。

如果需要附加的数字数据使用std::to_string功能。

如果你想在能够序列化的任何对象为字符串,然后使用std::stringstream类的形式更加灵活。但是,你需要实现自己的流媒体运营商的功能它与自己的自定义类的工作。

的std :: string的+ =不为const char *(“添加字符串”什么东西一样显得),所以肯定使用字符串流是最接近需要什么样的工作 - 你只需要使用<<代替+

:一种方便字符串生成器用于C ++

像许多人面前回答的std ::字符串流是首选的方法。 它的工作原理很好,有很多的转换和格式化选项。 IMO它有一个非常不方便的缺陷,但:你不能把它作为一个衬垫或表达式。 你总是写:

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

这是非常恼人,尤其是当你想在构造函数初始化字符串。

的原因是,一个)的std :: stringstream的没有转换算到std ::串和字符串流不返回字符串流参考,但一个std的的b)经营<<():: ostream的引用,而不是 - 不能被进一步计算为一个字符串流

该解决方案是重写的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 )

甚至在构造。

我不得不承认我没有测量的表现,因为我还没有在其中使用了大量的字符串建设的又一个环境中使用它,但我相信它不会比的std :: stringstream的差很多,因为一切都通过引用完成(除了转换为字符串,但那在STD复制操作:: stringstream的为好)

我想添加一些新的东西,因为如下:

在第一次尝试,我没能击败

std::ostringstream 's operator<<

效率,但有更多的尝试我能够做StringBuilder,速度更快,在某些情况下。

每次我加上一个字符串我只是储存的参考它的地方和增加计数总额的大小。

真实的方式我终于实现了它(恐怖!) 是使用不透明的缓冲区(std::矢量 < char>):

  • 1字节的标题(2位来告诉我们,如果以下列数据:移string,string或字节[])
  • 6位来告诉长字节[]

对于字节[]

  • 我直接存储字节的短串(顺序存的访问)

对于移动琴弦 (strings所附带 std::move)

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

字符串

  • 指针指向一个 std::string 目(没有所有权)

还有一个小化,如果最后插入串章会,它检查免费保留但未使用的字节和储存进一步字节,在那里,而不是使用不透明的缓冲区(这是拯救一些记忆,它实际上使它稍微慢一些,也许也取决于CPU,这是难得一见串额外的保留空间,无论如何)

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

  • 我假定固定长度char类型(所1、2或4个字节,不好UTF8),我并不是说它不会的工作UTF8,只是我不检查了它的懒惰。
  • 我使用的编码糟糕的练习(不透明的缓冲器,容易使它不便携式,我相信我的是便携式的方式)
  • 缺乏所有特征 ostringstream
  • 如果某些引用的字符串中删除之前mergin所有的弦乐:未定义的行为。

结论呢?使用 std::ostringstream

它已经修复的最大的瓶颈,同时ganing几个百分点在的速度与我的执行情况是不值得的负面影响。

scroll top