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?

Author: An̲̳̳drew, 2010-03-17

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.

 37
Author: iain,
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

 121
Author: jk.,
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();
 75
Author: Stu,
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.

 34
Author: dan04,
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ą.

 9
Author: Andy Shellam,
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.

 5
Author: Daemin,
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ć

 2
Author: sergeys,
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.
 1
Author: Igor,
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.

 0
Author: GameDeveloper,
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)

 0
Author: user2328447,
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