C++ эквивалент StringBuffer / StringBuilder?

есть ли класс библиотеки стандартных шаблонов C++, который обеспечивает эффективную функциональность конкатенации строк, аналогичную c#'s StringBuilder или Java StringBuffer?

10 ответов


Примечание этот ответ получил недавно некоторое внимание. Я не выступаю за это как решение (это решение, которое я видел в прошлом, до 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. или вы можете использовать строго типизированный функтор, который инкапсулирует это и предоставляет строку.Формат как интерфейс например boost:: format


строка std::.функция 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# ' S StringBuilder Я считаю snprintf (или sprintf если вы хотите рискнуть написать ошибочный код ; -)) в массив символов и преобразовать обратно в строку-это единственный вариант.


С std::string в C++ является изменяемым, вы можете использовать это. У него есть += operator и


std:: string's += не работает с const char* (что такое "строка для добавления"), поэтому определенно использование stringstream ближе всего к тому, что требуется - вы просто используете


на трос контейнер может стоить, если нужно вставить / удалить строку в случайное место строки назначения или для длинных последовательностей символов. Вот пример из реализации 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 ' s operator<<

эффективность, но с большим количеством попыток я смог сделать StringBuilder, который в некоторых случаях быстрее.

каждый раз, когда я добавляю строку, я просто сохраняю ссылку на нее где-то и увеличиваю счетчик общего размера.

реальный способ, которым я, наконец, реализовал его (ужас!) использовать непрозрачный буфер (std:: vector ):

  • 1 байтовый Заголовок (2 бита, чтобы сказать, если следующие данные: перемещенная строка, строка или байт [])
  • 6 бит, чтобы сказать длину байта[]

для byte [ ]

  • я храню непосредственно байт коротких строк (для последовательного доступа к памяти)

для перемещенных строк (строки добавляются с std::move)

  • указатель на a std::string объект (у нас есть собственность)
  • установите флаг в классе, если там есть неиспользуемые зарезервированные байты

для строк

  • указатель на

удобный строитель строк для c++

как и многие люди ответили раньше, std:: stringstream является методом выбора. Он работает хорошо и имеет много вариантов преобразования и форматирования. ИМО имеет один довольно неудобный недостаток: вы не можете использовать его как один лайнер или как выражение. Вы всегда должны писать:

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

, который очень раздражает, особенно, когда вы хотите инициализировать строки в конструкторе.

причина является ли, что a) std::stringstream не имеет оператора преобразования в std::string и b) оператор

решение состоит в том, чтобы переопределить 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, так как все делается через ссылки (кроме преобразования в string, но это операция копирования в std::stringstream)