C++ odpowiednik StringBuffer / StringBuilder?
Czy istnieje standardowa Klasa biblioteki szablonów C++, która zapewnia wydajną funkcjonalność łączenia łańcuchów, podobną do StringBuilder w języku C#lub StringBuffer w języku Java?
10 answers
Zauważ, że ta odpowiedź otrzymała ostatnio pewną uwagę. Nie jestem zwolennikiem tego rozwiązania (jest to rozwiązanie, które widziałem w przeszłości, przed STL). Jest to interesujące podejście i powinno być stosowane tylko nad std::string
lub std::stringstream
, Jeśli po profilowaniu kodu odkryjesz, że powoduje to poprawę.
Zwykle używam albo std::string
lub std::stringstream
. Nigdy nie miałem z nimi żadnych problemów. Normalnie zarezerwowałbym najpierw trochę miejsca, jeśli znam szorstkie rozmiar sznurka z góry.
Widziałem, jak w odległej przeszłości inni ludzie tworzyli swój własny, zoptymalizowany konstruktor strun.
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;
}
};
Używa dwóch łańcuchów, jeden dla większości łańcuchów, a drugi jako obszar scratch do łączenia krótkich łańcuchów. Optymalizuje on dodawanie poprzez grupowanie krótkich operacji dodawania w jeden mały ciąg, a następnie dołączanie go do głównego ciągu, zmniejszając w ten sposób liczbę ponownych przydziałów wymaganych na głównym ciągu, gdy się powiększa.
I nie wymagaj tej sztuczki z std::string
lub std::stringstream
. Myślę, że był używany z trzecią biblioteką string przed std:: string, to było tak dawno temu. Jeśli przyjmiesz strategię taką jak ten profil, najpierw Twoja aplikacja.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-25 17:16:19
Sposobem C++ byłoby użycie std::stringstream lub po prostu zwykłych konkatenacji łańcuchowych. Ciągi C++ są zmienne, Więc względy wydajności konkatenacji są mniej niepokojące.
Jeśli chodzi o formatowanie, można zrobić wszystkie te same formatowanie na strumieniu, ale w inny sposób, podobny do cout
. możesz też użyć silnie wpisanego functora, który enkapsuluje to i dostarcza ciąg znaków.Format jak interfejs np. boost:: format
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-06 12:40:18
Std::string.funkcja dołączania nie jest dobrą opcją, ponieważ nie akceptuje wielu form danych. Bardziej użyteczną alternatywą jest użycie std: stringstream, w ten sposób:
#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();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-12 16:52:58
std::string
jest odpowiednikiem C++: jest zmienny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-09 13:30:43
Możesz użyć .append() służy do łączenia łańcuchów.
std::string s = "string1";
s.append("string2");
Myślę, że możesz nawet zrobić:
std::string s = "string1";
s += "string2";
Jeśli chodzi o operacje formatowania w C#'S StringBuilder
, uważam, że snprintf
(lub sprintf
Jeśli chcesz zaryzykować napisanie błędnego kodu ;-) ) do tablicy znaków i przekonwertowanie z powrotem na łańcuch jest jedyną opcją.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-17 14:23:14
Ponieważ std::string
W C++ jest zmienny, możesz go użyć. Posiada funkcję += operator
i append
.
Jeśli chcesz dołączyć dane liczbowe, użyj funkcji std::to_string
.
Jeśli chcesz uzyskać jeszcze większą elastyczność w postaci możliwości serializacji dowolnego obiektu do ciągu znaków, Użyj klasy std::stringstream
. Ale będziesz musiał zaimplementować własne funkcje operatora strumieniowego, aby mógł pracować z własnymi klasami niestandardowymi.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-29 02:08:13
Std:: string ' s + = nie działa z const char* (jakie rzeczy jak "string to add" wydają się być), więc zdecydowanie używanie stringstream jest najbliższe temu, co jest wymagane - wystarczy użyć
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-27 12:19:06
Kontener Lina może być wart, jeśli trzeba wstawić/usunąć łańcuch w losowym miejscu docelowego łańcucha lub dla długich sekwencji znaków. Oto przykład z implementacji 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.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-24 15:42:15
Chciałem dodać coś nowego ze względu na:
Podczas pierwszej próby nie udało mi się pokonać
std::ostringstream
's operator<<
Wydajność, ale przy większej liczbie prób udało mi się zrobić StringBuilder, który w niektórych przypadkach jest szybszy.
Za każdym razem, gdy dodaję ciąg po prostu przechowuję odniesienie do niego gdzieś i zwiększam licznik całkowitego rozmiaru.
Prawdziwy sposób, w jaki w końcu go zrealizowałem (Horror!) jest użycie nieprzezroczystego bufora (std:: vector ):
- 1-bajtowy nagłówek (2 bity, aby określić, czy następujące dane są: przesunięty łańcuch, łańcuch lub bajt [])
- 6 bitów do określenia długości bajtu []
dla bajtów [ ]
- zapisuję bezpośrednio bajty krótkich łańcuchów (dla sekwencyjnego dostępu do pamięci)
for moved strings (strings appended with std::move
)
- wskaźnik do
std::string
obiektu (mamy własność) - Ustaw flagę w klasie, jeśli są nieużywane zarezerwowane bajty tam
for strings
- wskaźnik do
std::string
obiektu (bez własności)
Jest też jedna mała optymalizacja, jeśli ostatni wstawiony ciąg został przeniesiony, sprawdza, czy nie ma wolnych zarezerwowanych, ale nieużywanych bajtów i przechowuje tam dalsze bajty zamiast używać nieprzezroczystego bufora (ma to na celu zaoszczędzenie pamięci, w rzeczywistości czyni go nieco wolniejszym, może zależeć również od procesora, i rzadko można zobaczyć Ciągi z dodatkową zarezerwowaną przestrzenią anyway)
To było w końcu nieco szybsze niż std::ostringstream
ale ma kilka minusów:
- założyłem, że typy znaków o stałej długości (więc 1,2 lub 4 bajty, nie są dobre dla UTF8), nie mówię, że nie będzie działać dla UTF8, tylko nie sprawdzałem go pod kątem lenistwa.
- użyłem bad coding practice (nieprzezroczysty bufor, łatwy do zrobienia, że nie jest przenośny, moim zdaniem jest przenośny przy okazji) {22]}
- Brak wszystkich funkcji
ostringstream
- jeśli jakiś odnośnik zostanie usunięty przed merginem wszystkich struny: nieokreślone zachowanie.
Wniosek? użycie
std::ostringstream
To już naprawić największe wąskie gardło, a ganing kilka % punktów prędkości z kopalni implementacji nie jest warte minusy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-30 12:55:05
Wygodny string builder dla c++
Jak Wiele osób odpowiedziało wcześniej, std:: stringstream jest metodą wyboru. Działa dobrze i ma wiele opcji konwersji i formatowania. IMO ma jednak jedną dość niewygodną wadę: nie można go używać jako jednej wkładki ani jako wyrażenia. Zawsze musisz napisać:
std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );
Co jest dość irytujące, szczególnie, gdy chcesz zainicjalizować ciągi znaków w konstruktorze.
Powodem jest to, że a) std:: stringstream nie ma operatora konwersji na std::string i b) operator
Rozwiązaniem jest nadpisanie STD:: stringstream i nadanie jej lepiej dopasowanych operatorów:
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;
}
Dzięki temu możesz pisać takie rzeczy jak
std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )
Nawet w konstruktorze.
Muszę przyznać, że nie mierzyłem wydajności, ponieważ mam nie użyto go w środowisku, które jeszcze mocno wykorzystuje string building, ale zakładam, że nie będzie to dużo gorsze niż std:: stringstream, ponieważ wszystko odbywa się za pomocą referencji (z wyjątkiem konwersji na string, ale jest to również operacja kopiowania w std::stringstream)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-02 01:32:00