Einleitung
ASCII nach binär
Fehler bei der Binär-Konvertierung
Binär nach ASCII
String zerlegen
Beispiel und Übung
Das Einlesen von Daten von der Tastatur eine recht komplizierte Sache, insbesondere dann, wenn man alle Fehleingaben abfangen möchte. Der sicherste Weg zum Einlesen von Daten führt deshalb meistens über das Einlesen einer kompletten Zeile und deren anschließender Auswertung. Hierbei können dann ASCII-Zahlen in binäre Zahlen konvertiert werden oder auch umgekehrt.
|
|
Den Eingabestream cin haben Sie ja schon kennen gelernt. Er liest im Prinzip Daten von der Tastatur ein und legt die Eingaben dann in den entsprechenden Variablen ab, d.h. auch cin führt intern eine Umwandlung von 'ASCII-Zahlen' in binäre Zahlen (so wie sie der Rechner verarbeitet) durch. Um nun einen String in seine 'Einzelteile' zu zerlegen, benötigen wir fast die gleiche Funktionalität wie cin, nur dass die Eingabe jetzt nicht über die Tastatur erfolgt sondern als String vorliegt.
Und genau diese Aufgabe erfüllt der Stream istringstream. Wenn Sie den Stream istringstream einsetzen müssen Sie die Header-Datei sstream einbinden. Um nun einen String in seine 'Einzelteile' zu zerlegen, definieren Sie zunächst ein istringstream Objekt. Anschließend übergeben Sie dem Objekt mittels der Memberfunktion str(...) den zu zerlegenden String. Und ab diesem Zeitpunkt verhält sich der istringstream genauso wie der Eingabestream cin, d.h. Sie extrahieren mithilfe des Operators >> die einzelnen Wörter bzw. Werte aus diesem Stream. Im nachfolgenden Beispiel enthält danach die Variable intVar1 den Wert 123, intVar2 den Wert 456 und dblVar den Wert 3.4.
|
// Header-Datei für istringstream einbinden #include <sstream> ... // Stream-Objekt definieren std::istringstream is; // string definieren und initalisieren std::string s1 = "123 456 3.4"; // String dem Stream-Objekt uebergeben is.str(s1); // Variablen für die Konvertierung definieren int intVar1, intVar2; double dblVar; // String nach binär konvertieren is >> intVar1 >> intVar2 >> dblVar; |
Soweit der 'Trivialfall'. Wenn Sie im Programm nun mehrere Strings zu konvertieren haben, so wird Ihr erster Versuch meistens so verlaufen, dass Sie dem bereits bestehenden Stream-Objekt mittels der Memberfunktion str(...) einfach einen neuen String übergeben und diesen dann in der gleichen Art und Weise auswerten wollen. Dies wird mit (an Sicherheit grenzender Wahrscheinlichkeit) fehlschlagen. Der Grund dafür liegt darin, dass nach dem kompletten Auslesen eines Strings der Stream istringstream den Status EOF (=End Of File) besitzt und damit sich quasi in einem Fehlerzustand befindet. Um diesen Status aufzuheben, rufen Sie nach der Auswertung des ursprünglichen Strings die Memberfunktion clear() des Stream-Objekts auf. Danach ist das Stream-Objekt 'wie neu' und kann erneut verwendet werden.
Da istringstream und cin außerdem vom gleichen Grundstream abstammen, können Sie beim istringstream auch die gleichen Manipulatoren verwenden wie beim Eingabestream cin. In letzter Konsequenz heißt dies, dass Sie mit dem istringstream genau das Gleiche erreichen können, was Sie auch mit dem Eingabestream cin können, nur dass die 'Daten' nun aus einem String stammen und nicht von der Tastatur kommen. Nachfolgend sehen Sie ein Beispiel, bei dem Hex-Zahlen aus einem String ausgelesen werden. Beachten Sie dabei, dass der Hex-Präfix 0x.. im String dabei optional vorhanden sein darf.
|
// Header-Datei für istringstream einbinden #include <sstream> ... // Stream-Objekt definieren std::istringstream is; // String dem Stream-Objekt übergeben is.str("0x50 10"); // String nach binär konvertieren int var1, var2; is >> hex >> var1 >> var2; cout << var1 << ' ' << var2; |
|
80 16 |
Wenn Sie wollen, können Sie einem istringstream Objekt bei seiner Definition auch gleich einen String übergeben. Selbstverständlich kann diesem Stream-Objekt jederzeit ein weiterer String übergeben, wenn Sie, wie vorher erwähnt, den EOF-Status des Streams mittels clear() aufheben.
|
// Header-Datei für istringstream einbinden #include <sstream> ... // Stream-Objekt definieren und initialisieren istringstream is("12 13"); // String nach binär konvertieren int var1, var2; is >> var1 >> var2; // Status des Stream-Objekts zurücksetzen is.clear(); // Neuen String dem Stream-Objekt zuweisen is.str("22 33"); ... |
Der Stream istringstream besitzt u.a die beiden Memberfunktionen fail() und eof(), mit deren Hilfe Fehler bei der Auswertung des Strings festgestellt werden können.
Fangen wir mit dem einfachsten Fehlerfall an, dass zu wenige Daten im String enthalten sind. Im Beispiel unten enthält der String (mit dem der Stream initialisiert wird) nur einen 'Wert', während anschließend versucht wird, aus dem Stream zwei Werte auszulesen. In diesem Fall liefert die Memberfunktion fail() den Wert true, da dies als Fehler interpretiert wird. Dieser Rückgabewert wird im Beispiel mittels einer if-Anweisung ausgewertet.
|
// Header-Datei für istringstream einbinden #include <sstream> ... // Stream-Objekt definieren und initialisieren istringstream is("10"); // Konvertieren nach binär int var1, var2; is >> var1 >> var2; // Fehlerabfrage if (is.fail()) { // Fehlerbehandlung cout << "Fehler: " << is.str() << endl; ... } |
Im obigen Beispiel finden Sie übrigens ebenfalls die bereits erwähnte string Memberfunktion str(), diesmal jedoch ohne Parameter. Wird str() ohne Parameter aufgerufen so liefert sie Ihnen einen Verweis auf den Eingabepuffer des istringstream-Objekts. Über diesen Verweis können Sie dann z.B. den Inhalt des Eingabepuffers ausgeben.
|
|
Sehen wir uns den nächsten Fehlerfall an, dass der Stream anstelle einer 'ASCII-Zahl' einen Text enthält. Auch hier liefert die Memberfunktion fail() den Wert true zurück, der dann entsprechend ausgewertet werden kann.
|
// Stream-Objekt definieren und initialisieren istringstream is("Emil"); // Versuch der Konvertierung nach binär int var; is >> var; if (is.fail()) ... // Fehlerbehandlung |
Und weiter geht's mit den Fehlern. Wieder soll versucht werden, eine 'ASCII-Zahl' nach binär zu konvertieren. Der String enthält nun aber am Anfang zwar 'ASCII-Zahlen', jedoch folgt unmittelbar darauf ein Text. In diesem Fall hilft uns die Memberfunktion fail() nicht weiter, da die Konvertierung zunächst erfolgreich ist; es werden die 'ASCII-Zahlen' am Anfang des Strings richtig nach binär konvertiert. Um nun feststellen zu können, ob der komplette String konvertiert wurde, wird die Memberfunktion eof() eingesetzt. Sie liefert nur dann true zurück, wenn alles konvertiert werden konnte, d.h. keine Reste im Stream 'stehen geblieben' sind.
|
// Stream-Objekt definieren und initialisieren istringstream is("12Emil"); // Konvertieren nach binär int var; is >> var; if (is.eof()==false) ... // Fehlerbehandlung // Stream zurücksetzen is.clear(); |
Nachfolgend sehen noch ein weiteres Beispiel (diesmal ohne Fehler), das aus einem String eine Zahl nach der anderen nach binär konvertiert. Tritt während dieser Konvertierung ein Fehler auf, so liefert die Memberfunktion fail() true zurück und die Konvertierungsschleife wird verlassen. Ebenfalls beendet wird die Schleife, wenn alle 'ASCII-Zahlen' konvertiert wurden, da in diesem Fall eof() true zurückliefert. Nach dem Verlassen der Schleife kann durch erneutes abprüfen des Streamstatus festgestellt werden, ob die Schleife durch einen Fehler oder durch erreichen des String-Endes verlassen wurde. In beiden Fällen müssen Sie aber den Stream mittels clear() zurücksetzen wenn Sie ihn weiter verwenden wollen.
|
// Stream-Objekt definieren und initialisieren istringstream is("11 22 33"); int var; // Kompletten String nach binär konvertieren while (is.eof() == false) { is >> var; if (is.fail()) break; cout << var << ','; } // Falls Fehler aufgetreten if (is.fail()) ... // Fehlerbehandlung // Stream zurücksetzen is.clear(); |
Sehen wir uns nun den umgekehrten Fall, die Konvertierung von binär nach ASCII. Auch diese Konvertierung kennen Sie prinzipiell schon durch den Ausgabestream cout. Er konvertiert für die Ausgabe binäre Daten nach ASCII. Wir müssen nun 'nur' noch einen Stream finden, der die Daten anstatt auf die Standardausgabe auszugeben diese als String ablegt.
Und genau diese Aufgabe erfüllt der Stream ostringstream. Wenn Sie den Stream ostringstream einsetzen müssen Sie wieder die gleiche Header-Datei sstream einbinden wie beim istringstream. Um Daten in einen String zu konvertieren, definieren Sie zunächst ein ostringstream Objekt. Anschließend können Sie dann die Daten genauso wie beim cout-Stream 'ausgeben', nur dass die Daten jetzt nicht auf die Standardausgabe gelangen sondern im Stream verbleiben. Um auf den Inhalt des Streams (das ist der entsprechende String mit den konvertierten Daten) zuzugreifen, verwenden Sie ebenfalls die von istringstream bekannte Memberfunktion str().
|
// Header-Datei für ostringstream einbinden #include <sstream> ... // Stream-Objekt definieren ostringstream os; // Daten in Stream übertragen int var = 12; os << "Wert" << var << '\n'; // Streaminhalt ausgeben cout << os.str(); |
Sehen wir und zuerst einmal an, wie eine Eingabe, die aus mehreren Wörtern bestehen kann, in einzelne Strings zerlegt wird. Nach dem Einlesen der Zeile mittels der Funktion getline(...) wird ein istringstream Objekt definiert, das mit dem eingelesenen String (Eingabezeile) initialisiert wird. Wie Sie bereits erfahren haben, kann mithilfe eines solchen istringstream Objekts ein String in seine 'Einzelteile' zerlegt werden. Der Unterschied zu den vorherigen Beispielen ist, dass nun der String nicht in binäre Daten konvertiert wird sondern in einzelne string Objekte. Um den kompletten eingelesenen String in einzelne Wörter zu zerlegen, wird so lange aus dem istringstream ausgelesen, bis dessen Memberfunktion eof() false zurückliefert. Die einzelnen Wörter werden dann mittels des Operators >> in ein string Objekt geschrieben. Wenn Sie wollen, können Sie das Beispiel auch einmal in Ihren Compiler übernehmen und laufen lassen.
|
//Header-Dateien einbinden #include <iostream> #include <string> #include <sstream> using namespace std; // main() Funktion int main() { // Stringobjekte definieren string line, word; // Komplette Zeile von der Standard-Eingabe einlesen getline(cin,line); // Eingabe an istringstream übergeben istringstream is(line); // Eingabe zerlegen while (!is.eof()) { // Einzelnes Wort aus Eingabe-String extrahieren is >> word; cout << word << endl; } } |
Mithilfe des istringstream können aber auch zum Beispiel Teile eines Strings, die durch ein frei definierbares Trennzeichen voneinander getrennt sind, extrahiert werden. Der 'Trick' ist dabei Folgender:
Die Funktion getline(...) wurde bisher nur zum Einlesen einer kompletten Zeile verwenden. Aber getline(...) kann eigentlich noch wesentlich mehr. getline(...) liest zunächst aus einem beliebigen Input-Stream eine Zeile aus, d.h. die Funktion getline(...) kann sowohl vom Eingabestream cin wie auch vom Dateistream ifstream oder sogar vom istringstream Stream eine Zeile auslesen.
Aber das ist für unserem Fall nur die halbe Miete; wir wollen ja nicht eine ganze Zeile in einem String ablegen sondern vorerst nur den Teil bis zum ersten Trennzeichen. Und dazu kann an getline(...) im dritten Parameter das Trennzeichen mitgegeben werden, bis zu dem einlesen werden soll. Standardmäßig ist dies der Zeilenvorschub '\n'. Wir brauchen nun anstelle des Zeilenvorschubs nur noch das gewünschte Trennzeichen im dritten Parameter angeben, und das war's dann auch schon. Zugegeben, das ist eigentlich schon etwas für Fortgeschrittene, aber Sie sehen, wenn man sich etwas intensiver mit Strings und Streams befasst kann einem das eine Menge Arbeit sparen.
|
// Zu untersuchende Zeile, Trennzeichen ist hier das Komma const char* const pLINE = "Agathe Mueller,Gansweg 23,12345 ADorf"; // 3 string Objekte definieren string name, street, city; // Zeile in einenm istringstream übertragen istringstream is(pLINE); // Aus istringstream einlesen mit Komma als Trennzeichen getline(is,name,','); // Weiter einlesen bis zum nächsten Trennzeichen getline(is,street,','); // Nun den Rest einlesen getline(is,city); ... |
|
![]()
Xaver;Xelsbrot;Xaver@speedy.de;Marmeladen Allee 5;
12345; AStadt |
Aus dieser Datei werden die Namen und die Email-Adressen herausgesucht und ausgegeben.
Dazu werden die einzelnen 'Datensätze' zeilenweise eingelesen. Aus der eingelesenen Zeile werden dann über den istringstream die durch Semikolon getrennten Datenfelder extrahiert.
Wenn Sie das Beispiel ausführen wollen, legen Sie sich im Root-Verzeichnis des VC++ Projekts die Datei adresse.csv mit obigem Inhalt an.
|
Name: Xaver Xelsbrot EMail: Xaver@speedy.de |
|
// Beispiel zur String-Konvertierungen #include <iostream> #include <fstream> #include <string> #include <sstream> using std::cout; using std::endl; using std::string; using std::getline; // Konstante für Dateiname const char* const pFILENAME = "adresse.csv"; // main() Funktion int main() { // Eingabedatei öffnen std::ifstream inFile; inFile.open(pFILENAME); if (!inFile) { cout << "Datei " << pFILENAME << " kann nicht geöffnet werden!" << endl; exit(1); } // Komplette Datei bearbeiten do { // Zeile aus der Datei string line; // Zu extrahierende 'Felder' aus der Dateizeile string firstname, lastname; string email; // Konvertierungsstream std::istringstream is; // Zeile einlesen getline(inFile,line); // Bei erreichen des Dateiendes den Rest überspringen if (inFile.eof()) continue; // Eingelesene Zeile in Konvertierungsstream übertragen is.str(line); // Nun Name und Email-Adresse aus der Zeile extrahieren getline(is,firstname,';'); getline(is,lastname,';'); getline(is,email,';'); // und ausgeben cout << "Name: " << firstname << ' ' << lastname << '\n'; cout << "EMail: " << email << '\n'; cout << "=========================================" << endl; } while (!inFile.eof()); // Datei wieder schliessen inFile.close(); } |
Ihre Aufgabe ist es nun, von der Tastatur einen int-Wert und einen double-Wert einzulesen und dabei eventuelle Fehleingaben abzufangen. Wurden die Werte richtig eingegeben, so sind diese dann um 10 erhöht auszugeben. War die Eingabe fehlerhaft, so sind entsprechende Meldungen auszugeben.
Testen Sie Ihr Programm mit dem folgenden, türkis dargestellten, Eingaben. lconv ist im Beispiel das Name des Programms.
|
lconv lconv lconv lconv lconv Hinweis: Die Eingaben sind türkis dargestellt. |