Stringverarbeitung in C++



  • Vorbemerkungen

    In C gibt es keinen eigenständigen Datentyp für Strings. Stattdessen werden char-Pointer als Beginn einer Zeichenkette interpretiert, die von der angegebenen Adresse bis zum nächsten Null-Element reichen. Dieses Vorgehen ist fehleranfällig, da die Speicherverwaltung komplett in den Händen des Anwenders liegt und von Seiten der Bibliothek nicht kontrolliert werden kann.

    Im Gegensatz dazu kümmert sich die String-Klasse der C++-Bibliothek selbst um ihren Speicher. Sie wächst mit, wenn die Zeichenkette verlängert werden soll, und sie merkt sich auch selbst, wieviel Speicherplatz sie ohne Komplikationen nutzen darf.

    Inhalt

    1. Basisklassen
    2. Grundfunktionen
    3. Suchfunktionen
    4. STL-Unterstützung
    5. Character Traits
    6. Hilfsdefinitionen
    7. Erweiterungen

    1 Basisklassen

    Header:

    #include <string>
    

    Definitionen

    template<typename charT>
    class char_traits;
    
    template<typename charT, typename traitT = char_traits<charT>, class alloc = allocator<charT> >
    class basic_string;
    typedef basic_string<char>    string;
    typedef basic_string<wchar_t> wstring;
    

    Die Klasse char_traits definiert die nötigen Grundfunktionen zum Vergleichen, Zuweisen und Verarbeiten von Zeichen und Zeichenketten. Die Standard-Implementierung nutzt üblicherweise die C-Funktionen wie memcmp() und memcpy() für ihre Arbeit. Die meisten Funktionen des Strings nutzen ihre Methoden für die Arbeit. Weitere Informationen zu den Character Traits fasse ich in Kapitel 5 zusammen.

    Die Klasse basic_string und ihre Spezialisierungen string (für char) bzw. wstring (für wchar_t) bieten die eigentliche Funktionalität eines Strings. Sie verwenden die Methoden der Traits-Klasse (beim Defaultwert letztendlich die Stringfunktionen der C-Bibliothek) für die Zeichenverarbeitung und die Methoden der Allocator-Klasse (die Defaultklasse basiert auf new[] und delete[]) für die Speicherverwaltung.

    Anmerkung: Im Folgenden bezeichnet "String" eine beliebige Ausprägung des basic_string-Klassen-Templates, "Zeichen" den dazugehörigen Zeichentyp und "C-String" einen String in C-Semantik (nullterminierte Zeichenfolge).
    Außerdem gelten alle Aussagen für std::string und char analog auch für wstring und wchar_t und jede andere Spezialisierung der String-Klasse.

    2 Grundfunktionen

    Alle wichtigen Grundfunktionen für den Umgang mit Zeichenketten wurden in die String-Klasse aufgenommen. An Stellen, die String-Eingaben benötigen, sind mehrere Parameterkombinationen möglich:

    • const string& str - String
      Kopie eines anderen Strings
    • const string& str,size_t idx,size_t len - Teilstring
      Teilstring aus einem anderen String (len Zeichen ab Position idx)
    • const char cstr* - C-String
      Kopie eines C-Strings (nullterminierte Zeichenfolge)
    • const char cstr,size_t len* - char-Array
      Kopie eines char-Arrays (len Zeichen, '\0' ist normaler Wert)
    • char c - Einzelzeichen
      ein einzelnes Zeichen
    • size_t len,char c - Zeichenblock
      len Kopien des Zeichens c
    • Iterator beg,Iterator end - Bereich
      Kopie aus dem Iterator-Bereich [beg,end[

    Folgende Parameterkombinationen sind dabei je nach verwendeter Methode erlaubt:

    Methode            String    Teil-     C-String  char-     Zeichen   Zeichen-  Iterator-
                                 string              Array               block     Bereich
    
    constructor        ja        ja        ja        ja         -        ja        ja
    
    operator =         ja         -        ja         -        ja         -         -
    assign()           ja        ja        ja        ja         -        ja        ja
    
    operator +=        ja         -        ja         -        ja         -         -
    append()           ja        ja        ja        ja         -        ja        ja
    push_back()         -         -         -         -        ja         -         -
    
    insert() (Index)   ja        ja        ja        ja         -        ja*        -
    insert() (Iter.)    -         -         -         -        ja        ja*       ja
    
    replace() (Index)  ja        ja        ja        ja        ja        ja         -
    replace() (Iter.)  ja         -        ja        ja         -        ja        ja
    
    find() etc.        ja         -        ja        ja        ja         -         -
    
    operator +         ja         -        ja         -        ja         -         -
    
    Vergleiche         ja         -        ja         -         -         -         -
    compare()          ja        ja        ja        ja         -         -         -
    
    (*) eventuell mehrdeutig, siehe Kapitel 4
    

    Anmerkung: Außer bei Übergabe eines C-Strings zählt das '\0'-Zeichen als ganz normaler Wert, kann also auch mitten in einem String vorkommen.

    2.1 Konstruktion und Zuweisung von Strings

    Die Konstruktoren für Strings unterstützen alle Kombinationen außer einem einzelnen Zeichen - dafür kann alternativ der (size_t,char)-Konstruktor verwendet werden, indem als Anzahl eine 1 übergeben wird:

    string s('x');  //FEHLER
    string s(1,'x');//OK, legt eine Kopie von 'x' an
    

    Eine weitere Möglichkeit, einen String anzulegen, bietet die Methode substr(idx=0,len=MAX). Diese kopiert maximal len Zeichen (wenn der zweite Parameter fehlt: alle Zeichen bis zum Stringende) ab der Position idx in einen neuen String.

    Zuweisungen zu einem bestehenden String können entweder mit dem Operator = (für Strings, C-Strings und Zeichen) oder der Methode assign() (für alle Kombinationen außer Einzelzeichen) durchgeführt werden.

    2.2 Stringverkettungen

    An einen vorhandenen String können mit dem Operator += (für Strings, C-Strings und Zeichen), der Methode append() (für alle Kombinationen außer Einzelzeichen) oder der Methode push_back() (für Zeichen) weitere Zeichen angehängt werden. Alle Funktionen kümmern sich selbst darum, dass der reservierte Speicherplatz des Strings mitwächst.

    Außerdem können mit dem Operator + zwei bestehende Strings bzw. ein String mit einem C-String oder Einzelzeichen zusammengefügt werden.

    Achtung: Aufgrund der Art, wie C++ seine Operatoren auswertet, ist es nicht möglich, zwei C-Strings auf diese Weise zusammenzufassen (C-Strings sind weiterhin char-Pointer und lassen sich nicht addieren). Um das Problem zu umgehen, reicht es aus, einen der beteiligten Strings in einen std::string umzuwandeln:

    string str = "Hallo" + "Welt"; // Fehler
    string str = string("Hallo")+"Welt"; //klappt
    

    Anmerkung: Solange es sich um String-Literale handelt, kann man sich auch auf die Vorverarbeitung durch den Präprozessor verlassen, der nebeneinander stehende Literale zusammenfasst:

    string str = "Hallo" "Welt";//klappt - aber nur mit Literalen
    

    2.3 Einfügen, Löschen und Ersetzen von Zeichen

    Strings bieten die Methode insert(), um in der Mitte ihrer Daten neue Elemente einzufügen. Die Zielposition kann dabei entweder als Index oder als Iterator angegeben werden, die einzufügenden Daten in der Indexversion in allen Kombinationen außer Einzelzeichen und Iteratorbereich, in der Iteratorversion als Einzelzeichen, Zeichenblock oder als Iteratorbereich.

    Zum Löschen von Zeichen haben Strings die Methode erase(), die

    • einen Indexwert (löscht alle Zeichen hinter dieser Position)
    • einen Indexwert und die Länge (löscht entsprechend viele Zeichen ab dem Index)
    • einen Iterator (löscht das Zeichen an der gegebenen Position)
    • zwei Iteratoren (löscht den Bereich dazwischen)

    als Parameter erhalten kann und die Methode clear(), die den kompletten String löscht.

    Eine weitere Methode, Zeichen zu löschen, bietet resize() an - wenn die Stringlänge verkürzt wird, werden entsprechend viele Zeichen am Ende des Strings gelöscht.

    Aus der Kombination von Löschen und Einfügen ergibt sich das Ersetzen von Teilstrings. Die Methode replace() bekommt die Position der zu ersetzenden Zeichen entweder als Index und Länge oder als Iteratorbereich und die einzufügenden Daten in allen Kombinationen außer Iteratorbereich (Indexversion) bzw. Teilstring und Einzelzeichen (Iteratorversion). Wenn sich beim Ersetzen die Stringlänge nicht ändern soll, können die Zeichen auch per Direktzugriff bzw. über Iteratoren überschrieben werden.

    Um alle Vorkommen einer Zeichenkette durch denselben Wert zu ersetzen, müssen die Methoden find() und replace() in einer Schleife zusammengefasst werden:

    void replace_all(string& text,const string& fnd,const string& rep)
    {
      size_t pos = text.find(fnd);
      while(pos!=string::npos)
      {
        text.replace(pos,fnd.length(),rep);
        pos = text.find(fnd,pos+rep.length());
      }
    }
    

    Einzelne Zeichen können auch mit dem Algorithmus replace() oder replace_if() ersetzt werden.

    2.4 Stringvergleich

    Strings können miteinander verglichen werden und sortieren sich dabei nach lexikografischer Reihenfolge. Dazu werden sämtliche Vergleichsoperatoren überladen, um entweder zwei Strings oder einen String mit einem C-String vergleichen zu können.

    Des Weiteren kann die Methode compare() (für Strings, Stringteile, C-Strings oder char-Arrays) verwendet werden, um einen kompletten String oder einen Teil des Strings (gegeben durch Startindex und Länge) mit anderen Werten zu vergleichen. Diese Methode gibt je nach Vergleichsergebnis einen negativen Wert (*this ist kleiner als der String), 0 (beide Strings sind gleich) oder einen positiven Wert (*this ist größer als der String) zurück.

    2.5 Zeichenzugriff und C-String-Anbindung

    Strings bieten mit dem Index-Operator [] und der Methode at() vollen Zugriff auf die einzelnen Zeichen, die sie verwalten. Der Unterschied zwischen beiden Versionen ist wie bei vector<> und deque<> die Fehlerkontrolle - operator[] gibt Datenmüll zurück, wenn der angegebene Index größer oder gleich der aktuellen Stringlänge ist, at() wirft eine out_of_range-Exception.

    Anmerkung: Bei konstanten Strings ist length() ein legaler Parameter für operator[] - und gibt den Wert \0 zurück. Bei nichtkonstanten Strings und für die Methode at() liegt dieser Index außerhalb des zulässigen Bereiches und führt zu undefiniertem Verhalten bzw. einer Exception.

    Um die Daten eines Strings als char-Array nutzen zu können, gibt es die Methoden data() und c_str() (beide liefern einen const char* auf die internen Daten, c_str() garantiert außerdem, daß der Bereich nullterminiert ist) sowie copy(buf,len,idx=0) (kopiert maximal len Zeichen ab Position idx in ein extern verwaltetes char-Array - ohne Nullterminator).

    Um den Inhalt eines Strings als veränderbares char-Array verwenden zu können, sind etwas mehr Verrenkungen notwendig. Versuchen Sie deshalb nach Möglichkeit, sie zu vermeiden:

    string str;
    str.resize(100);
    sprintf(&str[0],"Ich bin ein String: %i",4711);
    

    Anmerkung: Im normalen Gebrauch ist nicht garantiert, dass die Zeichendaten des Strings mit '\0' abgeschlossen werden. Der String verwaltet seine Länge üblicherweise seperat und hängt das Schlußzeichen für C-Strings nur an, wenn es notwendig ist - z.B. beim Aufruf der Methode c_str().

    2.6 Größe und Kapazität

    Genau wie Vektoren verwalten Strings ihre Daten in einem zusammenhängenden Speicherbereich und können vorsorglich mehr Platz anfordern, als sie tatsächlich benötigen. Dadurch ergeben sich drei Größen, die für einen String gesetzt und abgefragt werden können.

    Die physikalische Größe des Strings ist die Anzahl an gespeicherten Zeichen ohne eventuell vorhandenen Null-Terminator (Strings müssen ihre Daten nicht mit '\0' abschließen, außer zur Ausgabe durch c_str()). Die Größe kann über die Methoden size() und length() abgefragt (beide Methoden sind gleichwertig) und über resize(len,c) gesetzt werden - letzteres schneidet Zeichen am Ende ab bzw. füllt den String mit Kopien von c (Defaultwert ist '\0') auf, um die nötige Größe zu erreichen. Außerdem beeinflussen viele Stringmethoden, z.B. replace(), append() oder erase(), indirekt auch die Größe des Strings.

    Die Kapazität des Strings ist die Zeichenzahl, die ohne Reallokation eingetragen werden kann. Sobald die Größe die Kapazität überschreitet, wird der gesamte Stringinhalt in einen neuen, größeren Block umkopiert und der alte Speicherblock freigegeben. Die Kapazität kann über die Methode capacity() abgefragt und über die Methode reserve(size) gesetzt werden. Im Gegensatz zu Vektoren, deren Kapazität NIE verkleinert wird, darf ein String auch in einen kleineren Speicherblock umziehen (allerdings nur auf Anforderung) - insbesondere bewirkt ein reserve() ohne Parameter eine Verkleinerung der Kapazität auf die aktuelle Größe.

    Anmerkung: Es ist im Standard nicht garantiert, dass der String seine Kapazität verringern muss - er darf eine entsprechende Anforderung auch ignorieren. Umgekehrt ist es jedoch nicht erlaubt, dass der String aus eigenem Antrieb die Kapazität verkleinert. Der einzige Weg, garantiert die Kapazität eines Strings zu veringern, ist die Verwendung des sogenannten "swap-Tricks":

    void shrink(string& str)
    {
      string nstr(str.begin(),str.end());
      str.swap(nstr);
    }
    

    Die Maximalgröße eines Strings ist die maximale Anzahl an Zeichen, die theoretisch in einem String untergebracht werden können. Jeder Versuch, dieses Limit zu überschreiten, verursacht eine length_error-Exception. Die Maximalgröße ist eine implementierungsspezifische Konstante und kann zur Laufzeit über die Methode max_size() abgefragt werden. Die einzige Möglichkeit, diesen Wert zu beeinflussen, ist der Einsatz eines anderen Allokators. Aber normalerweise ist der Grenzwert groß genug, um ihn im laufenden Betrieb nicht zu überschreiten.

    3 Suchfunktionen

    Suchfunktionen dienen dazu, bestimmte Zeichenfolgen oder -kombinationen in einem String zu finden. Alle Methoden, die ein String dazu anbietet, können als Suchwert Strings, C-Strings, char-Arrays oder Einzelzeichen entgegennehmen und erhalten einen optionalen Parameter, der den Startpunkt der Suche angibt (Defaultwert ist 0=Stringanfang):

    • find()
      sucht die übergebene Zeichenkette als Teilstring
      (entspricht dem Algorithmus find() (für Einzelzeichen) bzw. search() (für Teilstrings))
    • rfind()
      sucht die Zeichenkette als Teilstring (von hinten beginnend)
      (entspricht dem Algorithmus find() mit Reverse-Iteratoren (für Einzelzeichen) bzw. find_end() (für Teilstrings))
    • find_first_of()
      sucht das erste Zeichen des Strings, das in der Zeichenkette genannt wird
      (entspricht dem Algorithmus find_first_of())
    • find_first_not_of()
      sucht das erste Zeichen des Strings, das in der Zeichenkette nicht genannt wird
    • find_last_of()
      sucht das letzte Zeichen des Strings, das in der Zeichenkette genannt wird
      (entspricht dem Algorithmus find_first_of() mit Reverse-Iteratoren)
    • find_last_not_of()
      sucht das letzte Zeichen des Strings, das in der Zeichenkette nicht genannt wird

    Alle diese Funktionen geben den Index des gefundenen Zeichens zurück, bzw. die Konstante string::npos (siehe Kapitel 6.2), wenn sie nichts gefunden haben.

    4 STL-Unterstützung

    Die Stringklassen sind zwar unabhängig von der Standard Template Library entwickelt worden, können aber problemlos mit der STL zusammenarbeiten. Zu diesem Zweck bieten sie neben den stringspezifischen Methoden ein zu dem des Vektors vergleichbaren Interface an (das ist auch der Grund, warum die Methoden size() und length() nebeneinander existieren) - inklusive der Methoden insert() und push_back(), die den Einsatz von Insert-Iteratoren mit Strings ermöglichen.

    Außerdem hat die String-Klasse etliche Methoden, die mit Iteratoren arbeiten können:

    • begin() , end() , rbegin() und rend()
      liefern normale bzw. reverse Iteratoren auf den Anfang bzw. das Ende der Zeichenfolge
    • string(beg,end)
      konstruiert einen String aus dem Bereich [beg,end[
    • assign(beg,end) und append(beg,end)
      nehmen den Bereich [beg,end[ und weisen ihn dem String zu bzw. hängen ihn an
    • insert(pos,c) , insert(pos,num,c) und insert(pos,beg,end)
      fügen an der Position pos ein Zeichen c, num Kopien von c bzw. den Bereich [beg,end[ ein
      Achtung: der Aufruf "s.insert(0,n,'x');" ist mehrdeutig - 0 könnte entweder die Indexposition 0 oder den NULL-Zeiger (char* als String-Iterator) darstellen. Zur Auflösung dieser Mehrdeutigkeit muss der Wert richtig gecastet werden - oder man wechselt zur Iteratorversion der Methode:
    //korrekte Typangabe:
    str.insert((string::size_type)0,n,'x');
    //Iterator verwenden:
    str.insert(str.begin(),n,'x');
    
    • erase(pos) und erase(beg,end)
      löschen das Zeichen an Position pos bzw. den Bereich [beg,end[
    • replace(beg,end,str) , replace(beg,end,cstr) , replace(beg,end,carr,len) , replace(beg,end,num,c und replace(beg,end,nbeg,nend)
      ersetzen den Bereich [beg,end[ durch den String str, den C-String cstr, das char-Array carr aus len Zeichen, num Kopien von c bzw. den Bereich [nbeg,nend[

    String-Iteratoren bieten Random Access auf die verwalteten Zeichendaten. Auch wenn der C++-Standard sich nicht um Implementierungsdetails kümmert, dürften meistens char-Pointer dafür verwendet werden.

    5 Character Traits

    Die Character Traits einer String-Klasse bestimmen, wie diese mit ihren Zeichenwerten umgeht. Auf diese Weise lässt sich das Verhalten eines Strings anpassen, ohne in die Implementation des Zeichentyps eingreifen zu müssen (die Zeichenklassen liegen als eingebaute Typen außerhalb der Reichweite des Programmierers). Die Traits-Klasse besteht aus einer Sammlung von Typdefinitionen und statischen Methoden und operiert auf blanken char-Feldern. Die meisten ihrer Methoden entsprechen einer Funktion der C-Stringverarbeitung (bzw. greifen in der Standardversion sogar auf diese zurück).

    • char_type
      verwendeter Zeichentyp (char bzw. wchar_t)
    • int_type
      Hilfstyp für Fehlerausgaben (enthält mindestens einen Wert mehr als char_type, der für "end of file" steht) (int bzw. wint_t)
    • pos_type und off_type
      Hilfstypen für Positions- bzw. Offsetangaben in IO-Streams
    • state_type
      Hilfstyp für den Übersetzungsstatus in Multibyte-Streams
    • assign(tgt,src)
      Zeichenzuweisung (tgt=src)
    • eq(c1,c2) und lt(c1,c2)
      Zeichenvergleich (c1==c2 bzw. c1<c2)
    • length(str)
      Stringlänge (strlen(str))
    • compare(s1,s2,n)
      Stringvergleich (memcmp(s1,s2,n))
    • copy(s1,s2,n) und move(s1,s2,n)
      Stringkopie (memcpy(s1,s2,n) bzw. memmove(s1,s2,n))
    • assign(str,n,c)
      Stringzuweisung (n mal Zeichen c) (memset(str,c,n))
    • find(str,n,c)
      Zeichensuche im String (memchr(str,c,n))
    • eof()
      der Wert für end of file (EOF)
    • to_int_type(c) und to_char_type(i)
      Konvertierung zwischen int_type- und char_type-Darstellung ("to_char_type(eof())" ist undefniert)
    • not_eof(i)
      gibt alles außer eof() zurück (Zeichen werden 1:1 übergeben, "not_eof(eof())" ist implementationsspezifisch)
    • eq_int_type(i1,i2)
      Vergleich im int_type (i1==i2)

    Die Character Traits werden auch von den IO-Streams verwendet, deshalb enthalten sie auch Elemente, die für Strings eigentlich überflüssig sind (z.B. pos_type oder eof()).

    Indem eine eigene Traits-Klasse bereitgestellt wird, kann das Verhalten von Strings angepasst werden. Z.B. ist es möglich, den Vergleich von Zeichen(folgen) unabhängig von Groß- und Kleinschreibung zu implementieren:

    struct nocase_traits : public std::char_traits<char>
    {
      static bool eq(const char& c1,const char& c2)
      { return toupper(c1)==toupper(c2); }
      static bool lt(const char& c1,const char& c2)
      { return toupper(c1)<toupper(c2); }
    
      static int compare(const char* s1, const char* s2, size_t n)
      {
        for(size_t p=0;p<n;++p)
          if(!eq(s1[p],s2[p])) return lt(s1[p],s2[p])?-1:1;
        return 0;
      }
    
      static const char* find(const char* s, size_t n, const char& c)
      {
        for(size_t p=0;p<nn;++p)
          if(eq(s[p],c)) return s+p;
        return 0;
      }
    };
    
    typedef std::basic_string<char,nocase_traits> ncstring;
    
    //Ein-/Ausgabe von ncstring's
    inline ostream& operator<< (ostream& strm,const ncstring& s)
    { return strm << string(s.data(),s.length()); }
    
    inline istream& operator>> (istream& strm,ncstring& s)
    {
      string s2;strm>>s2;
      if(strm) s.assign(s2.data(),s2.length());
      return strm;
    }
    inline istream& getline(istream& strm,ncstring& s,char delim='\n')
    {
      string s2;getline(strm,s2,delim);
      if(strm) s.assign(s2.data(),s2.length());
      return strm;
    }
    

    Anmerkung: Ein- und Ausgabeoperatoren sind nur definiert, wenn der Zeichentyp und Traits-Typ von String und IO-Stream übereinstimmt. Deshalb müssen sie gesondert überladen werden, wenn Spezialstrings mit den "normalen" Streamklassen zusammenarbeiten sollen.

    6 Hilfsdefinitionen

    Strings stellen einige zusätzliche Definitionen zur Verfügung, die im Allgemeinen nicht benötigt werden. Für den professionellen Einsatz kann es jedoch ganz nützlich sein, diese Definitionen zu kennen.

    6.1 Hilfstypen

    Wie die STL-Container definieren auch Strings ein ganzes Sortiment an Hilfstypen, die besonders für die Arbeit mit generischen Stringtypen (z.B. in Template-Funktionen) verwendet werden können:

    • value_type (charT)
      der verwaltete Zeichentyp
    • traits_type (traitT)
      der Typ der verwendeten Zeichen-Traits-Klasse
    • size_type (size_t) und difference_type (ptrdiff_t)
      Zahlentypen für Indexangaben bzw. Differenzwerte
    • reference (charT&) und const_reference (const charT&)
      veränderbare bzw. konstante Referenzen auf einzelne Zeichen
    • pointer (charT*) und const_pointer (const charT*)
      veränderbare bzw. konstante Zeiger auf den Zeichentyp
    • iterator , const_iterator , reverse_iterator und const_reverse_iterator
      Iteratoren und Reverse-Iteratoren auf String-Elemente (typischerweise als Pointer implementiert)
    • allocator_type (alloc)
      der Typ der verwendeten Allokator-Klasse

    Anmerkung: Viele der Typdefinitionen werden aus der Allokator-Klasse übernommen.

    6.2 npos

    "string::npos" ist ein spezieller Wert vom Typ size_type. Dabei handelt es sich üblicherweise um (size_type)-1. Er wird von den String-Methoden für zwei Verwendungszwecke verwendet:

    Erstens dient er als Defaultwert für die Längenangabe bei Teilstring-Funktionen (z.B. erase(), replace() oder substr()) und definiert, daß der betrachtete Abschnitt bis zum Ende des Strings reichen soll.

    Zweitens wird npos von find() und seinen Varianten zurückgegeben, wenn der Suchstring nicht gefunden wurde. Beachten Sie, dass es für die Auswertung nicht sicher ist, die Ausgaben der Suchfunktionen in einen vorzeichenbehafteten Typ zu casten.

    string s;
    
    int p = s.find("sub");
    if(p == string::npos) //unsicher wg. Typanpassungen
      ...
    if(p == -1) //unsicher, klappt aber normalerweise
      ...
    
    string::size_type p = s.find("sub")
    if(p == string::npos) //einzige portable Version
      ...
    

    6.3 Allokator-Unterstützung

    Genau wie die Containerklassen der STL können Strings für verschiedene Methoden zur Speicherverwaltung eingerichtet werden. Dazu wird als dritter Template-Parameter dem Klassentemplate basic_string die verwendete Allokator-Klasse übergeben. Diese wird genutzt, um Speicher anfordern, initialisieren und freigeben zu können.

    Außerdem bieten Strings die Typdefinition allocator_type, die die verwendete Allokator-Klasse enthält, die Memberfunktion get_allocator(), die den Allokator des Strings zurückgibt, und einen optionalen Parameter für die meisten String-Konstruktoren (nur der Copy Constructor übernimmt den Allocator vom kopierten String).

    7 Erweiterungen

    Auch wenn Strings ein weites Gebiet der Zeichenverarbeitung abdecken, können sie noch lange nicht alles. Zum Beispiel bietet die Standard-Bibliothek keine direkte Unterstützung für reguläre Ausdrücke oder Textverarbeitung.

    Allerdings lassen sich die fehlenden Funktionen oft mit einfachen Mitteln selbst programmieren. Aufgaben der Textverarbeitung (z.B. einen String in Großbuchstaben zu übersetzen oder alle Vorkommen eines Wertes zu ersetzen) lassen sich mit Hilfe der STL-Algorithmen oder mit einer Schleifenstruktur wie in Kapitel 2.3 durchführen.

    Etwas aufwendiger ist es, den Einsatz von regulären Ausdrücken zu implementieren. Aber dafür gibt es eine ganze Reihe an Spezialbibliotheken, zum Beispiel Boost::Regex oder - als letzte Option - Boost::Spirit. Auch in die TR1 wurde die Verarbeitung von regulären Ausdrücken aufgenommen.



  • Boost bietet auch eine Bibliothek die sich nur mit Strings beschäftigt und viele der im Standard fehlenden Funktionen bereitstellt.
    http://www.boost.org/doc/html/string_algo.html



  • CStoll schrieb:

    void replace_all(string& text,const string& fnd,const string& rep)
    {
      size_t pos = text.find(fnd);
      while(pos!=string::npos)
      {
        text.replace(pos,pos+fnd.length(),rep);
        pos = text.find(fnd,pos);
      }
    }
    

    Der geht doch garnicht.
    Erst mal müsste es

    text.replace(pos,fnd.length(),rep);
    

    heißen.
    Und dann hat er das gleiche Problem wie viele andere replaceAll die man findet. Er endet in einer Endlosschleife, wenn man in "hallo" alle "a" mit "aa" ersetzt.
    Also sollte es

    pos = text.find(fnd, pos + rep.lenght());
    

    sein.



  • Ich glaub in keiner Sprache der Erde sind die Strings so mies designed wie in C++ 👎



  • Zumindest in C sind sie mieser.



  • Also ich finde Stringbehandung in Assembler am schlimmsten. Meine Erfahrung jedenfalls. 😃 Da kommen die C++-Strings am Ende garnicht so schlecht weg. 🙂



  • in C gibts do garkeine Strings. Nur Arrays von Zeichen.



  • @bemerkung: Hmm, da könntest du recht haben. (an unsere Korrekturleser: Warum hat mich da keiner drauf aufmerksam gemacht?)

    @bpl: Also ich bin mit den C++ Strings recht zufrieden (eleganter als die C-Variante sind sie allemal), aber ich kenne auch nicht jede andere Sprache der Welt, um so einen Vergleich ziehen zu können. Du etwa?

    (und wenn du meinst, es besser zu können - dann erklär doch mal, WAS dich an den C++ Strings stört)



  • CStoll schrieb:

    (an unsere Korrekturleser: Warum hat mich da keiner drauf aufmerksam gemacht?)

    weil die auch nur die üblichen beispiele kennen die man schnell findet. 😉 Google: std string replaceall



  • CStoll schrieb:

    (an unsere Korrekturleser: Warum hat mich da keiner drauf aufmerksam gemacht?)

    ui, das ist jetzt aber mal schlechter Stil. Den Fehler hast Du gemacht.



  • Jester schrieb:

    CStoll schrieb:

    (an unsere Korrekturleser: Warum hat mich da keiner drauf aufmerksam gemacht?)

    ui, das ist jetzt aber mal schlechter Stil. Den Fehler hast Du gemacht.

    Aber typisch Mensch. Bei eigenen Schwachstellen erst mal auf den anderen zeigen. Manche sind da richtig gut drin, so dass man es fast garnicht merkt. Geht jetzt nicht an CStoll.



  • CStoll meinte das mit Sicherheit eronisch...



  • Wo Menschen arbeiten, passieren Fehler. Ist egal, wer da jetzt Schuld hat oder nicht, war ja auch kein krasser Fehler.
    Code überfliege ich beim Korrekturlesen meistens nur (außer vom Autor anders gewünscht), da ich aus eigener Erfahrung davon ausgehe, dass der normal am Besten getestet ist.



  • Und wo bleibt die Korrektur?



  • Nur nicht hetzen, sonst stelle ich mich quer 😉

    Jester schrieb:

    ui, das ist jetzt aber mal schlechter Stil. Den Fehler hast Du gemacht.

    Ja, ich weiß, daß es mein Fehler war (und ich übernehme auch die Verantwortung dafür). Aber normalerweise werden unsere Artikel nochmal korrekturgelesen, bevor sie veröffentlicht werden - und da ist dieser Fehler keinem aufgefallen.



  • CStoll schrieb:

    Nur nicht hetzen, sonst stelle ich mich quer 😉

    Jester schrieb:

    ui, das ist jetzt aber mal schlechter Stil. Den Fehler hast Du gemacht.

    Ja, ich weiß, daß es mein Fehler war (und ich übernehme auch die Verantwortung dafür). Aber normalerweise werden unsere Artikel nochmal korrekturgelesen, bevor sie veröffentlicht werden - und da ist dieser Fehler keinem aufgefallen.

    Naja, wegen deinem Fehler ist bestimmt kein Unternehmen an der Börse pleite gegangen oder eine Spaceshuttel explodiert. Falls doch, würde ich an deiner Stelle die Verantwortung nicht wirklich auf mich nehmen!

    Achja, da fällt mir doch tatsächlich ein, das wir in unsere Artikel so ne Haftungsausschlussklausel einbauen sollten. 💡 🙄 😃 😉



  • Artchi schrieb:

    Achja, da fällt mir doch tatsächlich ein, das wir in unsere Artikel so ne Haftungsausschlussklausel einbauen sollten. 💡 🙄 😃 😉

    Da sollte eigentlich die allgemeine Aussage unter dem Forum ausreichen, oder?

    Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.



  • Upps! Naja, ist halt das Kleingedruckte... wird gerne überlesen. Weiß ich bescheid. 🙂


Anmelden zum Antworten