C++相当于StringBuffer/StringBuilder?

An̲*_*rew 164 c++ stl string-concatenation

是否有C++标准模板库类提供有效的字符串连接功能,类似于C#的StringBuilder或Java的StringBuffer

jk.*_*jk. 143

C++的方式是使用std :: stringstream或只是普通的字符串连接.C++字符串是可变的,因此连接的性能考虑因素不太重要.

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

  • __C++字符串是mutable__:完全正确."StringBuilder"存在的全部原因是[覆盖Java的不可变基本字符串类型的低效率](http://stackoverflow.com/questions/5234147/why-stringbuilder-when-there-is-string).换句话说`StringBuilder`是拼凑的,所以我们应该很高兴我们不需要在C++中使用这样的类. (49认同)
  • @bobobobo不变的字符串虽然有其他的好处,它的马匹用于课程 (49认同)
  • 不是普通的字符串连接创建一个新对象,所以与Java中的不变性相同的问题?在下面的例子中考虑所有变量都是字符串:a = b + c + d + e + f; 是不是要在b和c上调用operator +,然后在结果和d等上调用operator +? (8认同)
  • 坚持一分钟的人,标准字符串类知道如何改变自己,但这并不意味着效率低下不存在.据我所知std :: string不能简单地扩展其内部char*的大小.这意味着以需要更多字符的方式改变它需要重新分配和复制.它与字符向量没什么不同,在这种情况下保留你需要的空间当然更好. (7认同)
  • @TrygveSkogsholm - 它与字符向量没有区别,但当然字符串的"容量"可能大于其大小,因此并非所有附加都需要重新分配.通常,字符串将使用指数增长策略,因此追加仍然可以摊销到线性成本操作.这与Java的不可变字符串不同,在这些字符串中,每个追加操作都需要将两个字符串中的所有字符复制到一个新字符串中,因此一系列附加符号通常最终为"O(n)". (6认同)
  • @jk我认为将std :: string标记为const使其不可变,所以我称之为灵活性. (3认同)
  • @Hoten,据我所知,移动语义只会在从`operator +`返回时保存对象的复制.但是,仍然需要在`operator +`中创建新的字符串对象来存储字符串连接的结果. (2认同)

Stu*_*Stu 84

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();
Run Code Online (Sandbox Code Playgroud)


iai*_*ain 50

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

我通常使用std::stringstd::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;
    }
};
Run Code Online (Sandbox Code Playgroud)

它使用两个字符串,一个用于大多数字符串,另一个用作连接短字符串的临时区域.它通过将短附加操作批处理为一个小字符串然后将其附加到主字符串来优化附加,从而减少主字符串变大时所需的重新分配数量.

我没有用std::string或要求这个技巧std::stringstream.我认为它在std :: string之前与第三方字符串库一起使用,就在很久以前.如果你采用这样的策略,首先应用你的应用程序.

  • 重新发明轮子.std :: stringstream是正确的答案.看下面的好答案. (11认同)
  • @ Kobor42我同意你的看法,我在答案的第一行和最后一行指出. (9认同)

dan*_*n04 40

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


And*_*lam 10

您可以使用.append()来简单地连接字符串.

std::string s = "string1";
s.append("string2");
Run Code Online (Sandbox Code Playgroud)

我想你甚至可以做到:

std::string s = "string1";
s += "string2";
Run Code Online (Sandbox Code Playgroud)

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

  • @jk - 在比较 .NET 的 StringBuilder 的格式化能力时,它们是唯一的方法,这是原始问题特别提出的问题。我确实说过“我相信”,所以我可能是错的,但是你能告诉我一种在 C++ 中获得 StringBuilder 功能而不使用 printf 的方法吗? (2认同)

use*_*447 6

一个方便的 C++ 字符串生成器

就像之前回答的许多人一样,std::stringstream 是首选方法。它运行良好,并有很多转换和格式选项。IMO 但它有一个非常不方便的缺陷:你不能将它用作单衬或表达。你总是要写:

std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );
Run Code Online (Sandbox Code Playgroud)

这很烦人,尤其是当您想在构造函数中初始化字符串时。

原因是,a) std::stringstream 没有到 std::string 的转换运算符,并且 b) stringstream 的运算符 << () 不返回 stringstream 引用,而是返回 std::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;
}
Run Code Online (Sandbox Code Playgroud)

有了这个,你可以写这样的东西

std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )
Run Code Online (Sandbox Code Playgroud)

即使在构造函数中。

我必须承认我没有测量性能,因为我还没有在大量使用字符串构建的环境中使用它,但我认为它不会比 std::stringstream 差多少,因为一切都已完成通过引用(除了转换为字符串,但这也是 std::stringstream 中的复制操作)


Dae*_*min 5

因为std::string在C++中是可变的,你可以使用它.它具有+= operatorappend功能.

如果需要附加数值数据,请使用这些std::to_string函数.

如果您希望以能够将任何对象序列化为字符串的形式提供更大的灵活性,那么请使用std::stringstream该类.但是您需要实现自己的流操作符函数才能使用自己的自定义类.