Существует ли класс стандартной библиотеки шаблонов С++, который обеспечивает эффективную функциональность конкатенации строк, аналогичную С# StringBuilder или Java StringBuffer?
С++ эквивалент StringBuffer/StringBuilder?
Ответ 1
ПРИМЕЧАНИЕ. Этот ответ получил некоторое внимание в последнее время. Я не выступаю за это как решение (это решение, которое я видел в прошлом, перед 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, это было так давно. Если вы сначала примете стратегию, как этот профиль, свое приложение.
Ответ 2
Путь на С++ будет использовать std:: stringstream или просто простые конкатенации строк. Строки С++ являются изменяемыми, поэтому соображения производительности конкатенации менее опасны.
в отношении форматирования вы можете сделать все то же форматирование в потоке, но по-другому, подобно cout
. или вы можете использовать строго типизированный функтор, который инкапсулирует это и предоставляет интерфейс String.Format, например. boost:: format
Ответ 3
Функция 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();
Ответ 4
std::string
- эквивалент С++: он изменен.
Ответ 5
Вы можете использовать .append() для просто конкатенации строк.
std::string s = "string1";
s.append("string2");
Я думаю, что вы даже можете это сделать:
std::string s = "string1";
s += "string2";
Что касается операций форматирования С# StringBuilder
, я полагаю, что snprintf
(или sprintf
, если вы хотите рискнуть записать багги-код;-)) в массив символов и преобразовать обратно в строку, это единственное вариант.
Ответ 6
Так как std::string
в С++ изменен, вы можете использовать это. Он имеет функцию += operator
и append
.
Если вам нужно добавить числовые данные, используйте функции std::to_string
.
Если вы хотите еще большую гибкость в виде возможности сериализации любого объекта для строки, используйте класс std::stringstream
. Но вам нужно будет реализовать собственные функции оператора потоковой передачи, чтобы он работал с вашими собственными классами.
Ответ 7
std::string + = не работает с const char * (что может показаться, например, как "строка для добавления" ), поэтому определенно использование stringstream является самым близким к тому, что требуется - вы просто используете < < < < < < <; вместо +
Ответ 8
Контент Rope может стоить, если нужно вставить/удалить строку в случайное место целевой строки или для длинного char последовательностей. Вот пример реализации 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.
Ответ 9
Я хотел добавить что-то новое из-за следующего:
В первый момент я не смог победить
std::ostringstream
operator<<
но с большим количеством попыток я смог сделать StringBuilder быстрее в некоторых случаях.
Каждый раз, когда я добавляю строку, я просто храню ссылку на нее где-то и увеличиваю счетчик общего размера.
Реальный путь, который я, наконец, внедрил (Horror!), - использовать непрозрачный буфер (std::vector <char> ):
- 1 байтовый заголовок (2 бита, чтобы указать, следующие ли следующие данные: перемещенная строка, строка или байт [])
- 6 бит, чтобы рассчитать длину байта []
для байта []
- Я сохраняю непосредственно байты коротких строк (для доступа к последовательной памяти)
для перемещенных строк (строки, добавленные с помощью std::move
)
- Указатель на объект
std::string
(у нас есть собственность) - установить флаг в классе, если там используются неиспользуемые зарезервированные байты.
для строк
- Указатель на объект
std::string
(без права собственности)
Также существует небольшая оптимизация, если последняя вставленная строка была включена, она проверяет свободные зарезервированные, но неиспользуемые байты и хранит там дополнительные байты вместо того, чтобы использовать непрозрачный буфер (это чтобы сохранить некоторую память, она фактически делает это немного медленнее, возможно, зависит также от процессора, и редко можно видеть строки с дополнительным зарезервированным пространством)
Это было, наконец, немного быстрее, чем std::ostringstream
, но оно имеет несколько недостатков:
- Я предположил фиксированную длину char типов (так что 1,2 или 4 байта, не очень хорошо для UTF8), я не говорю, что это не сработает для UTF8. Просто я не проверял его на лень.
- Я использовал плохую практику кодирования (непрозрачный буфер, легко сделать его не переносным, я считаю, что мой переносимый кстати)
- Недостатки всех функций
ostringstream
- Если какая-либо ссылочная строка удаляется до слияния всех строк: undefined.
вывод? использование
std::ostringstream
Это уже устраняет наибольшее узкое место, в то время как удержание нескольких% очков в скорости с реализацией шахты не стоит недостатков.
Ответ 10
Удобный построитель строк для С++
Как и многие ранее ответившие, std:: stringstream - это метод выбора. Он работает хорошо и имеет множество вариантов преобразования и форматирования. IMO у него есть один довольно неудобный недостаток, хотя: вы не можете использовать его как один лайнер или как выражение. Вам всегда нужно писать:
std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );
что очень раздражает, особенно если вы хотите инициализировать строки в конструкторе.
Причина в том, что a) std:: stringstream не имеет оператора преобразования для std::string и b) оператора <() строкового потока не возвращают ссылку на строковый поток, а вместо этого ссылаются на ссылку 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;
}
С этим вы можете писать такие вещи, как
std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )
даже в конструкторе.
Я должен признаться, что не оценил производительность, поскольку я не использовал ее в среде, которая еще больше использует строковое построение, но я предполагаю, что это будет не намного хуже, чем std:: stringstream, поскольку все делается через ссылки (кроме преобразования в строку, но это также операция копирования в std:: stringstream)