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();
вы можете использовать .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
' 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)