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::.функция добавления не является хорошим вариантом, потому что она не принимает много форм данных. Более полезной альтернативой является использование 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();
вы можете использовать .append () для простого объединения строк.
std::string s = "string1"; s.append("string2");
Я думаю, вы могли бы даже быть в состоянии сделать:
std::string s = "string1"; s += "string2";
Что касается операций форматирования C#'s
StringBuilder
Я считаюsnprintf
(илиsprintf
если вы хотите рискнуть писать глючный код ; -)) в массив символов и конвертировать обратно в строку-это единственный вариант.
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
' soperator<<
эффективность, но с большим количеством попыток я смог сделать StringBuilder, который в некоторых случаях быстрее.
каждый раз, когда я добавляю строку, я просто сохраняю ссылку на нее где-то и увеличиваю счетчик общего размера.
реальный способ, которым я, наконец, реализовал его (ужас!) заключается в использовании непрозрачного буфер (std:: vector ):
- 1 байт Заголовок (2 бита, чтобы сказать, если следующие данные :перемещенная строка, строка или байт [])
- 6 бит, чтобы сказать длину байта[]
для Byte [ ]
- я храню непосредственно байт коротких строк (для последовательного доступа к памяти)
для перемещенных строк (строки добавляются с
std::move
)
- указатель на a
std::string
объект (у нас есть собственность)- установите флаг в классе, если там есть неиспользуемые зарезервированные байты
для строк
- указатель на a
std::string
объект (без права собственности)есть также одна небольшая оптимизация, если последняя вставленная строка была перемещена, она проверяет наличие свободных зарезервированных, но неиспользуемых байтов и сохраняет там дальнейшие байты вместо использования непрозрачного буфера (это для сохранения некоторой памяти, это на самом деле делает его немного медленнее, возможно, зависит также от процессора, и редко можно увидеть строки с дополнительным зарезервированным пространством в любом случае)
это было, наконец, немного быстрее, чем
std::ostringstream
но у него есть несколько недостатков:
- я предположил, что типы символов фиксированной длины (так 1,2 или 4 байта, не подходит для UTF8), я не говорю, что это не будет работать для UTF8, просто я не проверял его на лень.
- я использовал плохую практику кодирования (непрозрачный буфер, легко сделать его не портативным, я поверьте, мой портативный кстати)
- не хватает всех функций
ostringstream
- если какая-то ссылочная строка удаляется перед слиянием всех строк: неопределенное поведение.
вывод? использовать
std::ostringstream
это уже исправить самое большое узкое место в то время как ganing несколько % точек в скорости с реализацией шахты не стоит минусов.
удобный строитель строк для c++
как и многие люди ответили раньше, std:: stringstream-это метод выбора. Он работает хорошо и имеет много вариантов преобразования и форматирования. IMO у него есть один довольно неудобный недостаток: вы не можете использовать его как один лайнер или как выражение. Вы всегда должны написать:
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 )
даже в конструкторе.
у меня есть признаться, я не измерял производительность, так как я еще не использовал ее в среде, которая сильно использует string building, но я предполагаю, что это будет не намного хуже, чем std::stringstream, так как все делается через ссылки (кроме преобразования в string, но это также операция копирования в std::stringstream)