C ++ эквивалент StringBuffer / StringBuilder? - PullRequest
151 голосов
/ 17 марта 2010

Существует ли в C ++ стандартная библиотека шаблонов классов, обеспечивающая эффективную функциональность конкатенации строк, аналогично C # StringBuilder или Java StringBuffer ?

Ответы [ 10 ]

133 голосов
/ 17 марта 2010

C ++ может использовать std :: stringstream или просто конкатенации строк. Строки C ++ являются изменяемыми, поэтому соображения производительности при объединении менее важны.

Что касается форматирования, вы можете выполнять одинаковое форматирование в потоке, но другим способом, аналогичным cout. или вы можете использовать строго типизированный функтор, который инкапсулирует это и предоставляет интерфейс, подобный String.Format, например, boost :: format

79 голосов
/ 29 сентября 2011

Функция 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();
48 голосов
/ 17 марта 2010

ПРИМЕЧАНИЕ. Этот ответ недавно привлек к себе внимание. Я не защищаю это как решение (это решение, которое я видел в прошлом, до 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, это было так давно. Если вы применяете стратегию, подобную этому профилю, сначала подайте заявку.

38 голосов
/ 09 июня 2010

std::string - эквивалент C ++: он изменчив.

9 голосов
/ 17 марта 2010

Вы можете использовать .append () для простого объединения строк.

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

Я думаю, вы могли бы даже сделать:

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

Что касается операций форматирования в C # StringBuilder, я считаю, snprintf (или sprintf, если вы хотите рискнуть написать ошибочный код ;-)) в массив символов и преобразовать обратно в строку - это почти единственное опция.

5 голосов
/ 29 сентября 2011

Поскольку std::string в C ++ является изменчивым, вы можете использовать это. Имеет функцию += operator и append.

Если вам нужно добавить числовые данные, используйте функции std::to_string.

Если вам нужна еще большая гибкость в форме возможности сериализации любого объекта в строку, используйте класс std::stringstream Но вам нужно будет реализовать свои собственные функции оператора потоковой передачи, чтобы он мог работать с вашими собственными классами.

3 голосов
/ 27 октября 2011

std :: string's + = не работает с константным символом * (что вроде «строка для добавления» выглядит так), поэтому, безусловно, использование stringstream является наиболее близким к тому, что требуется - вы просто используете << вместо + </p>

2 голосов
/ 02 ноября 2017

Удобный конструктор строк для c ++

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

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

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

Причина в том, что a) std :: stringstream не имеет оператора преобразования в std :: string и b) операторы << () строкового потока не возвращают ссылку на строковый поток, но std :: ссылка ostream вместо этого - которая не может быть далее вычислена как поток строки. </p>

Решение состоит в том, чтобы переопределить 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)

1 голос
/ 24 января 2014

Контейнер Rope может быть полезен, если необходимо вставить / удалить строку в произвольном месте строки назначения или для длинных последовательностей символов. Вот пример из реализации 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.
0 голосов
/ 30 апреля 2015

Я хотел добавить что-то новое из-за следующего:

С первой попытки мне не удалось победить

std::ostringstream х operator<<

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

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

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

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

для байта []

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

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

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

для струнных

  • Указатель на std::string объект (без владения)

Существует также одна небольшая оптимизация: если последняя вставленная строка была перемещена, она проверяет наличие свободных зарезервированных, но неиспользуемых байтов и сохраняет там дополнительные байты вместо использования непрозрачного буфера (это для экономии памяти, это фактически делает он немного медленнее, может зависеть также от процессора, и в любом случае редко можно увидеть строки с дополнительным зарезервированным пространством)

Это было, наконец, немного быстрее, чем std::ostringstream, но у него было несколько недостатков:

  • Я предположил, что типы символов фиксированной длины (например, 1,2 или 4 байта, не годятся для UTF8), я не говорю, что это не будет работать для UTF8, просто я не проверял их на лень.
  • Я использовал плохую практику кодирования (непрозрачный буфер, легко сделать его не переносимым, я считаю, что мой, кстати, переносим)
  • Отсутствует все функции ostringstream
  • Если какая-либо строка, на которую ссылаются, удаляется перед объединением всех строк: неопределенное поведение.

вывод? использование std::ostringstream

Это уже устраняет самое большое узкое место, в то время как увеличение скорости на несколько% при реализации шахты не стоит минусов.

...