C++ equivalent van stringbuffer / stringbuilder?

Is er een C++ Standard Template Library-klasse die een efficiënte string-aaneenvoegingsfunctionaliteit biedt, vergelijkbaar met C # ‘S stringbuilder of java’s Stringbuffer ?


1, Autoriteit 100%

De C++ Way zou zijn om STD :: Stringstream of gewoon gewoon te gebruiken string aaneenschakelingen. C++ snaren zijn veranderd, zodat de prestatieoverwegingen van aaneenschakeling minder bezorgdheid zijn.

Met betrekking tot opmaak, kunt u dezelfde opmaak doen op een stroom, maar in een andere manier, vergelijkbaar met cout. Of u kunt een sterk getypte functor gebruiken die dit inkapselt en een string.format zoals interface b.v. Boost :: formaat


2, Autoriteit 59%

De std::string.appendFunctie is geen goede optie omdat het niet vele vormen van gegevens accepteert. Een nuttiger alternatief is het gebruik van std::stringstream; Zoals SO:

#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();

3, Autoriteit 30%

LET OP dit antwoord heeft de laatste tijd enige aandacht gekregen. Ik bepleit dit niet als een oplossing (het is een oplossing die ik in het verleden heb gezien, vóór de STL). Het is een interessante benadering en mag alleen worden toegepast op std::stringof std::stringstreamals je na het profileren van je code ontdekt dat dit een verbetering oplevert.

Normaal gebruik ik std::stringof std::stringstream. Ik heb met deze nooit problemen gehad. Normaal gesproken zou ik eerst wat ruimte reserveren als ik van tevoren de ruwe maat van de snaar weet.

Ik heb andere mensen in het verre verleden hun eigen geoptimaliseerde snarenbouwer zien maken.

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;
    }
};

Het gebruikt twee strings, één voor het grootste deel van de string en de andere als een scratch-gebied voor het aaneenschakelen van korte strings. Het optimaliseert de appends door de korte append-bewerkingen in één kleine string te bundelen en deze vervolgens toe te voegen aan de hoofdstring, waardoor het aantal hertoewijzingen dat nodig is op de hoofdstring wordt verminderd naarmate deze groter wordt.

Ik heb deze truc niet nodig met std::stringof std::stringstream. Ik denk dat het werd gebruikt met een reeks van een derde partij vóór Std :: String, het was zo lang geleden. Als u een strategie zoals dit profiel eerst uw aanvraag aanneemt.


4, Autoriteit 25%

std::stringis het C++ -quivalent: het is mutable.


5, Autoriteit 7%

U kunt het gebruik gebruiken () voor eenvoudig aaneenschrijdende snaren.

std::string s = "string1";
s.append("string2");

Ik denk dat je zelfs kunt doen:

std::string s = "string1";
s += "string2";

Wat betreft de opmaakbewerkingen van C # ‘s StringBuilder, ik geloof snprintf(of sprintfAls u een buggy-code wilt riskeren 😉 ) In een karakterarray en omzetten naar een string gaat over de enige optie.


6, Autoriteit 5%

Sinds std::stringin C++ is mutable U kunt dat gebruiken. Het heeft een += operatoren een appendfunctie toe.

Als u numerieke gegevens moet toevoegen, gebruikt u de std::to_stringfuncties.

Als u nog meer flexibiliteit wilt in de vorm van het kunnen serialiseren van elk object aan een tekenreeks en vervolgens de std::stringstreamKlasse gebruiken. Maar u moet uw eigen streaming-operatorfuncties implementeren om te werken met uw eigen aangepaste klassen.


7, Autoriteit 3%

een handige stringbuilder voor C++

Zoals veel mensen eerder beantwoord, is Std :: Stringsstream de methode van keuze.
Het werkt goed en heeft veel conversie- en opmaakopties. IMO heeft het echter een behoorlijk onhandige fout: je kunt het niet gebruiken als een one-liner of als een uitdrukking.
Je moet altijd schrijven:

std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );

wat behoorlijk vervelend is, vooral als je strings in de constructor wilt initialiseren.

De reden is dat a) std::stringstream geen conversie-operator heeft naar std::string en b) de operator << ()’s van de stringstream geven geen stringstream-referentie terug, maar in plaats daarvan een std::ostream-referentie – die niet verder kan worden berekend als een stringstream.

De oplossing is om std::stringstream te overschrijven en het beter passende operators te geven:

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;
}

Hiermee kun je dingen schrijven als

std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )

zelfs in de constructor.

Ik moet bekennen dat ik de prestaties niet heb gemeten, omdat ik het niet heb gebruikt in een omgeving die het zware gebruik van stringgebouw nog niet maakt, maar ik neem aan dat het niet veel slechter zal zijn dan std :: Stringsstream, sindsdien Alles gebeurt via referenties (behalve de conversie tot string, maar dat is ook een kopieeroperatie in STD :: Stringsstroom)


8, Autoriteit 2%

STD :: String’s + = werkt niet met const char * (wat spul like “string to adding” lijkt te zijn), dus absoluut gebruik van streamstroom is het dichtst bij wat nodig is – u gebruikt gewoon & lt; & lt; in plaats van +


9

De touw container kan de moeite waard zijn als ze reeks moeten invoegen / verwijderen de willekeurige plaats van bestemming string of voor een lange char-sequenties.
Hier is een voorbeeld van de implementatie van 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.

Other episodes