Существует ли класс стандартной библиотеки шаблонов C++, который обеспечивает эффективную функциональность конкатенации строк, аналогичную StringBuilder или StringBuffer Java а>?
С++ эквивалент StringBuffer/StringBuilder?
Ответы (10)
В C++ можно было бы использовать std::stringstream или просто конкатенацию строк. Строки C++ изменяемы, поэтому соображения производительности конкатенации не так важны.
что касается форматирования, вы можете сделать все то же форматирование в потоке, но другим способом , аналогично cout
. или вы можете использовать строго типизированный функтор, который инкапсулирует это и предоставляет интерфейс, подобный String.Format, например. boost::format
StringBuilder
заключается в том, чтобы скрыть неэффективность неизменного базового типа String в Java . Другими словами, StringBuilder
— это лоскутное одеяло, поэтому мы должны радоваться, что нам не нужен такой класс в C++.
- person bobobobo; 16.04.2013
operator+
. Однако внутри operator+
по-прежнему необходимо создать новый строковый объект для хранения результата конкатенации строк.
- person Serge Rogatch; 30.09.2015
O(n)
.
- person BeeOnRope; 18.11.2017
Функция 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();
ПРИМЕЧАНИЕ: этот ответ недавно привлек внимание. Я не пропагандирую это как решение (это решение я видел в прошлом, до 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, это было так давно. Если вы примете такую стратегию, сначала профилируйте свое приложение.
scratch
действительно что-то здесь делает. Количество перераспределений основной строки в значительной степени будет зависеть от ее конечного размера, а не от количества операций добавления, если только реализация string
не будет действительно плохой (т. е. не использует экспоненциальный рост). Таким образом, пакетирование append
не помогает, потому что, как только базовый string
станет большим, он будет только время от времени расти в любом случае. Кроме того, он добавляет кучу избыточных операций копирования и может больше перераспределять (следовательно, вызовы new
/delete
), так как вы добавляете короткую строку.
- person BeeOnRope; 18.11.2017
str.reserve(1024);
будет быстрее, чем эта штука
- person hanshenrik; 25.04.2019
std::string
является эквивалентом C++: он изменчив.
Вы можете использовать .append() для простого объединения строк.
std::string s = "string1";
s.append("string2");
Я думаю, вы могли бы даже сделать:
std::string s = "string1";
s += "string2";
Что касается операций форматирования StringBuilder
C#, я считаю, что snprintf
(или sprintf
, если вы хотите рискнуть написать код с ошибками ;-)) в массив символов и преобразовать обратно в строку - это почти единственный вариант.
Поскольку std::string
в С++ является изменяемым, вы можете использовать это. Он имеет функции += operator
и append
.
Если вам нужно добавить числовые данные, используйте функции std::to_string
.
Если вам нужна еще большая гибкость в виде возможности сериализовать любой объект в строку, используйте класс std::stringstream
. Но вам нужно будет реализовать свои собственные функции оператора потоковой передачи, чтобы он работал с вашими собственными пользовательскими классами.
Удобный построитель строк для C++
Как и многие люди, ответившие ранее, std::stringstream является предпочтительным методом. Он хорошо работает и имеет множество вариантов преобразования и форматирования. ИМО, у него есть один довольно неудобный недостаток: вы не можете использовать его как один лайнер или как выражение. Всегда нужно писать:
std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );
что довольно раздражает, особенно когда вы хотите инициализировать строки в конструкторе.
Причина в том, что а) std::stringstream не имеет оператора преобразования в std::string и б) оператор ‹‹() строкового потока не возвращает ссылку на строковый поток, а вместо этого возвращает ссылку на 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)
std::stringstream
так себя не ведет.
- person einpoklum; 07.06.2020
std::string's += не работает с const char* (какими вещами вроде «строка для добавления» кажется), поэтому определенно использование stringstream ближе всего к тому, что требуется — вы просто используете ‹‹ вместо +
Контейнер 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.
Я хотел добавить что-то новое из-за следующего:
С первой попытки я не смог победить
std::ostringstream
's operator<<
эффективности, но с большим количеством попыток я смог сделать StringBuilder, который в некоторых случаях работал быстрее.
Каждый раз, когда я добавляю строку, я просто где-то сохраняю ссылку на нее и увеличиваю счетчик общего размера.
Реальный способ, которым я, наконец, реализовал это (ужас!) - использовать непрозрачный буфер (std::vector ‹ char >):
- 1 байт заголовка (2 бита, чтобы определить, являются ли следующие данные: перемещенной строкой, строкой или байтом [])
- 6 бит, чтобы определить длину байта[]
для байта [ ]
- Я храню непосредственно байты коротких строк (для последовательного доступа к памяти)
для перемещенных строк (строки с добавлением std::move
)
- Указатель на объект
std::string
(у нас есть право собственности) - установить флаг в классе, если там есть неиспользуемые зарезервированные байты
для строк
- Указатель на объект
std::string
(без права собственности)
Также есть одна небольшая оптимизация: если последняя вставленная строка была перемещена, она проверяет свободные зарезервированные, но неиспользуемые байты и сохраняет там дополнительные байты вместо использования непрозрачного буфера (это делается для экономии памяти, на самом деле это немного замедляет работу). , возможно, зависит также от процессора, и в любом случае редко можно увидеть строки с дополнительным зарезервированным пространством)
Наконец, это было немного быстрее, чем std::ostringstream
, но у него было несколько недостатков:
- Я предполагал типы символов фиксированной длины (то есть 1,2 или 4 байта, не подходит для UTF8), я не говорю, что это не будет работать для UTF8, просто я не проверял это из-за лени.
- Я использовал плохую практику кодирования (непрозрачный буфер, его легко сделать непереносимым, кстати, я считаю, что мой переносимый)
- Не хватает всех функций
ostringstream
- Если какая-то строка, на которую ссылаются, удалена до объединения всех строк: поведение undefined.
вывод? использовать std::ostringstream
Это уже устраняет самое большое узкое место, в то время как увеличение скорости на несколько% с реализацией шахты не стоит недостатков.
std::ostringstream
. - person CoffeDeveloper   schedule 30.04.2015