Frage

Gibt es eine C ++ Standard Template Library-Klasse, die eine effiziente String-Verkettung Funktionalität, ähnlich wie C # bietet 's String oder Java

War es hilfreich?

Lösung

Hinweis diese Antwort hat einige Aufmerksamkeit kürzlich erhalten. Ich plädiere nicht als Lösung (es ist eine Lösung, die ich in der Vergangenheit gesehen habe, vor dem STL). Es ist ein interessanter Ansatz und sollte nur über std::string oder std::stringstream angewendet werden, wenn nach dem Code Profilieren Sie entdecken dies eine Verbesserung macht.

Ich benutze normalerweise entweder std::string oder std::stringstream . Ich habe noch nie irgendwelche Probleme mit ihnen hatten. Ich würde normalerweise etwas Platz reservieren zuerst, wenn ich die grobe Größe der Zeichenfolge im Voraus mit.

Ich habe andere Leute gesehen, ihre eigenen optimierten String-Builder in der fernen Vergangenheit zu machen.

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

Es wird zwei ein Zeichenfolgen für die Mehrheit der Saite und die andere als Scratch-Bereich für kurze Strings verketten. Es optimize des Appends durch Dosieren der kurzen append Operationen in einem kleinen String dann diese an die Haupt Zeichenfolge anhängt, wodurch die Anzahl von Umschichtungen auf der Hauptsaite erforderlich ist, verringert, da es größer wird.

Ich habe nicht erforderlich, diesen Trick mit std::string oder std::stringstream. Ich denke, es mit einer dritten Partei String-Bibliothek vor std verwendet wurde :: string, es war, dass vor langer Zeit. Wenn Sie eine Strategie, wie dieses Profil übernehmen Ihre Anwendung zuerst.

Andere Tipps

Die C ++ Art und Weise std :: string oder einfach zu bedienen wäre String-Verkettungen. C ++ Strings sind wandelbar, so dass die Leistungsaspekte der Verkettung sind weniger problematisch.

in Bezug auf die Formatierung, können Sie auf einen Stream alle die gleiche Formatierung, aber in eine andere Art und Weise, ähnlich wie cout . oder Sie können eine stark typisierte Funktors verwenden, die diese kapselt und bietet eine String.Format wie Schnittstelle z.B. boost :: Format

Die std :: string.append Funktion ist keine gute Wahl, weil es nicht viele Formen von Daten akzeptiert. Eine sinnvolle Alternative ist die Verwendung std: string, etwa 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();

std::string is die C ++ Äquivalent. Es ist wandelbar

Sie können mit .append () für einfach Strings verketten.

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

Ich glaube, Sie sogar in der Lage sein könnten, zu tun:

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

Wie für die Formatierungsoperationen von C # 's StringBuilder, glaube ich snprintf (oder sprintf, wenn Sie Gefahr schreiben fehlerhaften Code wollen ;-)) in ein Zeichenfeld und konvertieren zurück in einen String über die einzige Option ist.

Da std::string in C ++ wandelbar ist, können Sie diese verwenden. Es hat eine += operator und eine append Funktion.

Wenn Sie numerische Daten verwenden, um die std::to_string Funktionen anhängen müssen.

Wenn Sie noch mehr Flexibilität in Form wollen in der Lage zu sein, jedes Objekt in einen String serialise verwenden Sie dann die std::stringstream Klasse. Aber Sie müssen Ihre eigenen Streaming-Bedienfunktionen für sie zur Arbeit mit Ihren eigenen benutzerdefinierten Klassen implementieren.

std :: string des + = keine Arbeit mit const char * (was solche Sachen erscheinen „string hinzufügen“ zu sein), also auf jeden Fall mit string ist in der Nähe von, was erforderlich ist - man muss nur mit << statt +

Ein bequemer String-Builder für c ++

Wie viele Menschen vor antworteten std :: string ist die Methode der Wahl. Es funktioniert gut und hat viel Konvertierung und Formatierungsoptionen. IMO hat es eine ziemlich unbequem Fehler wenn: Sie es nicht als ein Liner oder als Ausdruck verwenden können. Sie haben immer zu schreiben:

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

das ist ziemlich ärgerlich, vor allem, wenn Sie Strings in dem Konstruktor initialisieren.

Der Grund dafür ist, dass a) std :: string hat keinen Konvertierungsoperator zu std :: string und b) der Operator << () 's des string keine string Referenz zurück, aber einen std :: ostream Referenz statt -., die nicht weiter als String Strom berechnet werden

Die Lösung ist std :: string außer Kraft zu setzen und ihm eine bessere Anpassung Operatoren zu geben:

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

Mit diesem können Sie schreiben Dinge wie

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

auch im Konstruktor.

Ich muss zugeben, dass ich nicht die Leistung messen, da ich es nicht in einer Umgebung verwendet haben, die noch bauen starke Nutzung von String macht, aber ich nehme an, es wird nicht viel schlimmer sein als std :: string, da alles wird über Referenzen erfolgt (außer der Umstellung auf Zeichenfolge, aber das ist ein Kopiervorgang in std :: string auch)

Die Rope Behälter kann sich lohnen, wenn zum Ein- / Lösch-String in die Zufallszeichenfolge der Bestimmungsort oder einen langen char Sequenzen. Hier ist ein Beispiel von SGI-Implementierung:

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.

wollte ich etwas Neues hinzuzufügen aus folgendem Grunde:

In einem ersten attemp scheiterte ich zu schlagen

std::ostringstream ‚s operator<<

Effizienz, aber mit mehr attemps konnte ich einen String machen, die schneller in einigen Fällen ist.

anhängen Jedesmal, wenn ich einen String Ich speichere nur einen Verweis irgendwo auf sie und den Zähler der Gesamtgröße erhöhen.

Die eigentliche Art, wie ich es endlich umgesetzt (! Horror) ist einen undurchsichtigen Puffer zu verwenden (std :: vector ):

  • 1 Byte-Header (2 Bits sagen, ob Daten finden: verschobene string, string oder byte [])
  • 6 Bits Länge von Byte zu sagen []

für byte []

  • Ich speichere direkt Bytes von kurzen Strings (für sequentiellen Speicherzugriff)

für verschobene Strings (Strings mit std::move angehängt)

  • Der Zeiger auf ein std::string Objekt (wir haben Eigentum)
  • ein Flag in der Klasse, wenn es ungenutzt reservierten Bytes gibt

für Strings

  • Der Zeiger auf ein std::string Objekt (kein Eigentum)

Es gibt auch eine kleine Optimierung, wenn zuletzt eingefügter Zeichenfolge in wurde mov'd, es prüft kostenlos reservierten, aber nicht genutzten Bytes und speichert weiteres Bytes dort anstelle den undurchsichtigen Puffer zu verwenden (dies ist etwas Speicher zu sparen, macht es tatsächlich es etwas langsamer, hängt vielleicht auch auf der CPU, und es ist selten Saiten mit extra reservierten Platz zu sehen sowieso)

Dies war schließlich etwas schneller als std::ostringstream aber es hat einige Nachteile:

  • Ich nahm an char-Typen feste Länge (also 1,2 oder 4 Bytes, nicht gut für UTF8), ich sage nicht, es wird nicht Arbeit für UTF8, Just Ich habe es nicht für Faulheit.
  • Ich habe schlechte Codierung der Praxis (opak Puffer, einfach nicht tragbar zu machen, ich glaube, mein durch die Art und Weise tragbar ist)
  • Lacks alle Funktionen von ostringstream
  • Wenn einige referenzierten Zeichenfolge gelöscht werden, bevor alle die Saiten mergin. Undefiniertes Verhalten

Schlussfolgerung? verwenden std::ostringstream

Es ist bereits der größte Engpass zu beheben, während ganing wenige% -Punkte in der Geschwindigkeit mit meiner Implementierung ist die Nachteile nicht wert.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top