string-Klasse
Strings definieren
Ein- und Ausgabe von Strings
String-Operationen
Beispiel und Übung
Zur Verarbeitung von Strings stellt die C++ Standard Bibliothek die Klasse string zur Verfügung. In den nachfolgenden Lektionen dieses Kurses werden zur Verarbeitung von Strings nur noch solche string Objekte verwendet und keine C-Strings mehr.
Sehen wir uns zunächst einmal an, was die string Klasse so interessant macht:
Die Klasse string enthält noch wesentlich mehr Eigenschaften als in dieser kurzen Einführung aufgezeigt werden kann. In einer späteren Lektion werden wir uns deshalb die Klasse string nochmals etwas genauer ansehen.
Wenn Sie die Klasse string in ihrem Programm einsetzen, so müssen Sie die mittels #include <string> die dazugehörige Header-Datei einbinden. Achten Sie aber unbedingt darauf dass hier kein .h steht! Die Header-Datei string.h bzw. ihr C++ Gegenstück cstring ist eine völlig andere Header-Datei!
Ferner müssen Sie beachten, dass die Klasse (der Datentyp) string im Namensraum std definiert ist, d.h. Sie müssen entweder std::string angeben oder aber die Anweisung using std::string; verwenden.
Aber sehen wir uns zunächst einmal an, wie string Objekte definierte werden.
|
Noch einmal zur Erinnerung: Damit in Zukunft nicht die totale Verwirrung bezüglich des Begriffs String ausbricht, wird im weiteren Verlaufe des Kurses unter einem String immer ein string Objekt verstanden. Zeichenfolgen die in char-Feldern abgelegt sind (das sind diejenigen, die mit einer binären 0 abgeschlossen sind) werden dagegen als C-String bezeichnet, weil dies unter C die einzige Möglichkeit war, veränderbare Zeichenfolgen abzulegen. Daraus folgt, dass ein char-Zeiger immer auf einen C-String zeigt und niemals auf einen String. |
Im Prinzip definieren Sie einen String genauso wie z.B. eine short-Variable, nur dass anstelle des Datentyps short nun der Datentyp string steht. Ein so definierter String enthält noch keine Zeichen, d.h. er ist leer.
Sie können einen String bei seiner Definition aber auch bereits mit einer Zeichenfolge initialisieren. Diese Initialisierung können Sie im Prinzip genauso durchführen wie das Initialisieren einer beliebigen anderen Variable, d.h. nach dem Stringnamen folgt der Zuweisungsoperator und danach nun die Zeichenfolge, mit der der String initialisiert werden soll. Diese Zeichenfolge kann entweder ein C-String Literal, eine C-String Konstante oder aber auch ein Zeiger auf ein char-Feld sein, in dem ein entsprechender C-String abgelegt ist.
Und selbstverständlich können Sie auch Stringfelder definieren. Beachten Sie dabei aber, dass das Feld dann n Strings aufnimmt, die auch unterschiedliche Längen besitzen können. Verwechseln dies nicht mit der Definition eines char-Feldes, das ja einen C-String mit maximal n-1 Zeichen aufnehmen kann.
Eine weitere Möglichkeit besteht darin, einen String mit einem bereits bestehenden String bei seiner Definition zu initialisieren, d.h. Sie erzeugen sich eine String-Kopie. Wie dies geht sehen in der letzten Definition im Beispiel.
|
// Header-Datei für string einbinden #include <string> using std::string; // Leeren String definieren string empty; // String mit C-String initialisieren string text1 = "Ein String"; // String Feld definieren string array[10]; // und erstem String einen C-String zuweisen array[0] = "String-Element 0"; // String mit einem anderen String initialisieren string text2(text1); |
War doch bis nicht so kompliziert, oder? Zugegeben, wir haben uns nur die einfachen Fälle angesehen um einen String zu erstellen. Die C++ Standard Bibliothek kennt acht verschiedene Arten ein string Objekt zu erstellen. Für unsere Zwecke sollten diese drei Möglichkeiten aber ausreichen. Mehr Infos finden Sie, wie immer, in der Online-Hilfe zur Klasse string.
Fangen wir mit dem einfachen Fall an, der Ausgabe von Strings.
Die Ausgabe eines Strings unterscheidet sich nicht von der Ausgabe von anderen Daten, d.h. Sie können den String direkt im Ausgabestream angeben.
|
#include <iostream> #include <string> using namespace std; int main() { string text = "Ein string-Objekt"; cout << text << endl; } |
Beim Einlesen eines Strings muss unterschieden werden, ob die einzelnen Wörter einer Eingabe (üblicherweise getrennt durch Leerzeichen) in mehreren Strings abgelegt werden sollen oder die ganze Eingabe (Zeile) in einem String.
Um einzelne Wörter einer Eingabe in Strings abzulegen, werden die Strings, genauso wie die bisherigen sonstigen Variablen, im Eingabestream angegeben. Und hier sehen Sie auch einen der Vorteile der Strings: egal ob ein Wort aus 4 Zeichen oder 80 Zeichen besteht, es wird automatisch genügend Speicher reserviert um das Wort im String ablegen zu können.
|
#include <iostream> #include <string> using namespace std; int main() { string word1, word2; cin >> word1 >> word2; } |
Wollen Sie eine ganze Zeile einlesen, so verwenden Sie hierfür die Funktion getline(...). Beachten Sie bitte, dass die hier verwendete Funktion getline(...) eine andere Signatur (Anzahl und Typ der Parameter) besitzt, als die Memberfunktion getline(...) des Eingabestreams cin. Die hier verwendete Funktion getline(...) enthält im ersten Parameter eine Referenz auf den Stream,, von dem die Zeile eingelesen werden soll. Im nachfolgenden Beispiel ist dies der Eingabestream cin. Aber ebenso möglich wäre hier z.B. die Angabe eines Dateistreams vom Typ ifstream. Der zweite Parameter ist dann die Referenz auf das string Objekt, in dem die Eingabe abgelegt wird. Und nochmals: egal wie lange die Eingabezeile ist, es wird immer genügend Speicher reserviert um die komplette Zeile im String ablegen zu können.
|
#include <iostream> #include <string> using namespace std; int main() { string line; getline(cin,line); } |
Die einfachste Operation ist die Zuweisung. So können Sie einem String ein C-String Literal, eine C-String Konstante oder auch einen anderen String zuweisen.
|
// Zwei Strings definieren string s1, s2; // C-String-Literal einem String zuweisen s1 = "Dies ist String1"; // String einem anderen String zuweisen s2 = s1; |
Ebenso ist es möglich, Strings einfach mithilfe des Operators + zu 'addieren', d.h. aneinander zu hängen. Zusätzlich zum Operator + stellt die Klasse string zur Addition noch den Operator += zur Verfügung.
|
// Zwei Strings definieren string s1("Text1"), s2; // C-String-Literal addieren, s1 danach "Text1 erweitert" s1 += " erweitert"; // Strings addieren, s2 danach "Text1 erweitert in s2" s2 = s1 + " in s2"; |
Um auf einzelne Zeichen innerhalb eines Strings zuzugreifen, verwenden Sie, wie bei den C-Strings, den Indexoperator[]. Auch hier hat das erste Zeichen im String den Index 0. Wollen Sie den gesamten String durchlaufen, so liefert die Memberfunktion size() Ihnen hierfür die Anzahl der Zeichen im String.
|
// String definieren und initialisieren string s1("Ein Text"); // Alle Zeichem im String einzeln ausgeben for (int i=0; i<s1.size(); i++) cout << s1[i] << ','; |
Strings werden mit den bekannten Vergleichsoperatoren <, >, == usw. verglichen. Der Vergleich erfolgt hierbei lexikalisch, d.h. 'Aaaa' ist kleiner als 'Bb'. Für einen der beiden Operanden des Vergleichsoperators kann anstelle eines string Objekts auch ein const char Zeiger stehen.
|
// Zwei String definieren und initialisieren string s1("Aaaa"), s2("Bb"); // Strings vergleichen if (s1 < s2) cout << "s1 kommt vor s2" << endl; else cout << "s2 kommt vor s1" << endl; |
Um einen String in einen const char-Zeiger zu konvertieren, besitzt die string Klasse die Memberfunktion c_str(). Mithilfe dieser Konvertierung können Sie also jetzt überall dort einen String verwenden, wo Sie ansonsten einen const char-Zeiger benötigen. Im Beispiel wird so z.B. der Inhalt eines Strings in ein char-Feld kopiert.
|
// String definieren und initialisieren string s1("Ein Text"); // char Feld definieren char charArray[40]; // Zeichen aus String in char-Feld umkopieren strcpy(charArray,s1.c_str()); |
Der umgekehrte Weg, also einen char-Zeiger in ein String zu konvertieren, erfolgt über den am Anfang dieser Lektion aufgeführten Zuweisungsoperator. Beachten Sie bitte, dass die Memberfunktion c_str() einen const char* liefert und Sie somit den Inhalt des Strings nicht über diesen Zeiger direkt verändern können.
Damit wollen wir an dieser Stelle die Einführung des string Datentyps abschließen. Der string Datentyp enthält, wie bereits erwähnt, noch zahlreiche weitere Memberfunktionen um z.B. Zeichen mitten im String einzufügen oder zu löschen oder sogar Zeichenfolgen in einem String zu suchen. Diese Memberfunktionen werden wir uns später in einer weiteren Lektion noch ansehen.
|
![]() Sollen in einem Programm je nach Inhalt einer ganzzahligen Variable verschiedene Anweisungen durchlaufen werden, so kann dies, wie nebenstehend dargestellt, durch geschachtelte IF-ELSE Anweisungen erfolgen. Im Beispiel werden je nachdem, ob die Variable var den Wert 1, 2 oder 3 enthält, die Anweisungen Aktion1, Aktion2 oder Aktion3 ausgefuehrt. Enthält var einen anderen Wert, so werden die Anweisungen Aktion4 ausgefuehrt. Aber das kennen Sie ja schon von der letzten Lektion her. |
|
Anzahl der Buchstaben in der Datei test.txt a: 32 b: 6 c: 8 d: 19 e: 72 |
|
// Lösung zur string Klasse #include <iostream> #include <iomanip> #include <fstream> #include <string> using std::cout; using std::endl; // Konstanten für Dateiname und Anzahl der Buchstaben im Alphabet const char *const pFILENAME = "test.txt"; const int NUMOFCHARS = 26; // main() Funktion int main() { // Datei öffnen std::ifstream inFile(pFILENAME); if (!inFile) { cout << "Kann Datei " << pFILENAME << " nicht öffnen!" << endl; exit(1); } // Zähler für die Anzahl der Buchstaben definieren und initialisieren int characters[NUMOFCHARS] = {0}; // Zähler für Sonderzeichen wie z.B. '.' oder ',' usw. int specialChar = 0; // Schleife zum Einlesen der Datei do { // String für eingelesene Zeile aus der Datei std::string line; // Eine Zeile aus der Datei einlesen std::getline(inFile,line); // Falls Dateiende nicht erreicht if (!inFile.eof()) { // Komplette Zeile durchlaufen for (unsigned int index=0; index<line.size(); index++) { // Falls Buchstabe im String a...z if ((line[index]>='a') && (line[index]<='z')) characters[line[index]-'a']++; else // Falls Buchstabe im String A...Z if ((line[index]>='A') && (line[index]<='Z')) characters[line[index]-'A']++; else // Sonderzeichen erhöhen specialChar++; } } } while (!inFile.eof()); // Datei auch wieder schliessen! inFile.close(); // Ergebnis ausgeben cout << "Anzahl der Buchstaben in der Datei " << pFILENAME << endl; // Alle Buchstabenzähler ausgeben for (int index=0; index<NUMOFCHARS; index++) { // Nach jedem 5. Buchstaben eine neue Zeile beginnen if (index%5 == 0) cout << endl; cout << static_cast<char>(index+'a') << ": " << std::setw(3) << characters[index] << '\t'; } // Noch Anzahl der Sonderzeichen ausgeben cout << "\nSonderzeichen: " << specialChar << endl; } |
So, jetzt dürfen Sie einmal Ihr ganzes Wissen einsetzen. Ziel der Übung ist es, ein Konvertierungsprogramm zu schreiben.
Das Programm soll die Textdatei gcc.mak einlesen (Inhalt folgt gleich), entsprechend den nachfolgenden Regeln konvertieren und das Ergebnis dann in der Datei conv.mak ablegen.
Die Konvertierung soll wie folgt erfolgen:
Für die Konvertierung Kleinbuchstaben -> Großbuchstaben ist die Bibliotheksfunktion int toupper(int nC); zu verwenden. Sie erhält das zu konvertierende Zeichen als Parameter übergeben und liefert als Returnwert das konvertierte Zeichen. Binden Sie dazu die Header-Datei cctype ein.
Nachfolgend der Inhalt der Datei gcc.mak. Wenn Sie das Beispiel ausführen wollen, erstellen Sie zuvor im Root-Verzeichnis des VC++ Projekts diese Datei.
![]() # # Note : this makefile is for gcc-2.95 and later ! # # # compiler # CC = gcc CXX = gcc # # Basename for libraries # LIB_BASENAME = libstlport_gcc # # guts for common stuff # # LINK=ar cr # 2.95 flag DYN_LINK=gcc -shared -o OBJEXT=o DYNEXT=so STEXT=a RM=rm -rf PATH_SEP=/ MKDIR=mkdir -p COMP=GCC$(ARCH) all: all_dynamic all_static include common_macros.mak WARNING_FLAGS= -Wall -W -Wno-sign-compare -Wno-unused -Wno-uninitialized -ftemplate-depth-32 |
Inhalt der Datei conv.mak nach erfolgter Konvertierung:
![]() CC = GCC CXX = GCC LIB_BASENAME = LIBSTLPORT_GCC LINK=AR CR DYN_LINK=GCC -SHARED -O OBJEXT=O DYNEXT=SO STEXT=A RM=RM -RF PATH_SEP=/ MKDIR=MKDIR -P COMP=GCC$(ARCH) ALL: ALL_DYNAMIC ALL_STATIC INCLUDE COMMON_MACROS.MAK WARNING_FLAGS= -WALL -W -WNO-SIGN-COMPARE -WNO-UNUSED -WNO-UNINITIALIZED -FTEMPLATE-DEPTH-32 |