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
Basisklassen
Grundfunktionen
Suchfunktionen
STL-Unterstützung
Character Traits
Hilfsdefinitionen
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,pos+fnd.length(),rep);
pos = text.find(fnd,pos);
}
}
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.
----
Bei deinem swap-Trick solltest du zuerst die gewünschte Kapazität bei nstr festlegen (bspw. str.size()).
Soweit ich weiß, legt der neue String seine Kapazität passend zur übergebenen Größe fest. Aber ich lasse mich gerne eines besseren belehren.
Außerdem ist deine Traits-Klasse für case-insensitive Strings nicht besonders gut, Begründung siehe die FAQ von Hume.
Ich weiß, er ist nicht komplett. Aber zu Demonstrationszwecken sollte es reichen (und im Gegensatz zu Hume's Beispielcode kann man Strings damit sogar nach "Größe" sortieren.
Des Weiteren ist im TR1 Regex-Unterstützung.
Wie ich schon früher erwähnte, habe ich die TR1 nicht
@estartu: Ja, von meiner Seite ist er fertig.