|
I-PROGR2 SS03 - PROGRAMMIEREN2 - Vorlesung mit Übung
|
In der heutigen VL sollen die Grundzüge der Input-Output-Funktionen von C++ erläutert werden. Gegenüber der Sprache C wurden diese vollständig überarbeitet. Das gesamte Material ist so umfangreich, dass es nicht in einer VL vollständig behandelt werden kann. Als primäre Quellen für diese VL dienen [JOSUTTIS 1999] UND [JOSUTTIS 2001].
Gegenüber der Sprache C wurden die I/OFunktionen von C++ u.a. internationalisiert, die stringbezogenen Operationen wurden ganz auf C++-Strings umgestellt, eine Ausnahmebehandlung wurde implementiert und alle Symbole gehören nun zum std-Namensbereich ('namespace').
Sämtliche input-Output-Prozesse werden in C++ als Streams behandelt, d.h. als 'Strom von Daten', wobei ein Stream als ein Objekt konstruiert ist, das bestimmte Eigenschaften hat und mittels bestimmter Streamfunktionen manipuliert werden kann.
Die grossen Unterscheidungen sind Input und Output. Diese werden zusätzlich unterschieden nach 'normalem' I/O, Datei-I/O und Stream-I/O.Zusätzlich gibt es für diese verschiedenen Streams auch noch Buffer, die mit den Streams verknüpft werden können. Eine allgemeine Übersicht gibt das nächste Schaubild:
I/O-Basisklassen/Templates
Man kann in dem Schaubild u.a. erkennen, dass eine allgemeine Input-Output-Klasse ios_base mit einem Template basic_ios<> aufgespalten wird in zwei Templates für input-stream und output-stream. Der input-stream ist zum Lesen von Daten und der output-stream zum Schreiben von Daten. Ein kombinierter input-output-stream ist auch verfügbar. Diese Basisklassen sind dann nochmals zusätzlich spezifiziert für Dateioperationen und Stringoperationen.
Entsprechend der Struktur von Klassen kann man bei einer Klasse neben den Attributen auch Elementfunktionen
unterscheiden. Die öffentlich zugänglichen Elementfunktionen der wichtigen I/O-Klassen zeigt das nächste Schaubild:
I/O-Basisklassen-Funktionen
Schon die Basisklasse ios_base stellt einige Funktionen zur Verfügung:
good() := Gibt 'true' zurück wenn das goodbit gesetzt ist.
fail() := Gibt 'true' zurück wenn das failbit or badbit gesetzt ist.
bad() := Gibt 'true' zurück wenn das badbit gesetzt ist.
eof() := Gibt 'true' zurück wenn das eofbit gesetzt ist.
rdstate := Gibt die aktuell gesetzten Flags zurück
clear() := Klärt (und setzt) alle (state) Flags
setstate(state) := Setzt zusätzliche state-Flags
operator void* () := Gibt zurück, ob der Stream nicht in einen Fehler gelaufen ist
operator ! () := Gibt zurück, ob der Stream in einen Fehler gelaufen ist
exceptions(flags) := Setzt flags, die Ausnahmen triggern
exceptions() := Gibt die flags zurück, die Ausnahmen triggern
tie() := Bindet zwei Streams zusammen
Zusätzlich haben die speziellen Klassen für Input bzw. Output spezielle Elementfunktionen:
Elementfunktionen für Input:
>> := Liest aus dem Stream
get() := liest das nächste Zeichen
getline() := liest ganze Zeile
read(count) := liest count-viele Zeichen
readsome(count) := liest bis zu count-viele Zeichen
gcount() := Gibt die Anzahl der gelesenen Zeichen durch die letzte read-Operation zurück
ignore() := ignoriert Zeichen
peek() := Liest das nächste Zeichen ohne es aus dem Stream zu entfernen
unget() := Setzt das letzte gelesene Zeichen wieder zurück in den Stream
putback() := wie unget()
Elementfunktionen für Output:
<< := Schreibt in den Stream
put(c) := Schreibt c in den Stream
write(count, str) := Schreibt count-viele Zeichen von str in den Stream
flush() := flushed die Puffer des Streams
Auf der Basis der zuvor definierten Klassen und Templates kann man Objekte generieren. Einige Objekte werden standardmässig generiert. Für den Inputstrema sind dies cin und wcin; für den output-stream sind dies cout, cerr und clog sowie die w-Versionen.
Für die Dateioperationen gibt es einige zusätzliche Operationen:
I/O-Basisklassen-Funktionen für Dateioperationen
Elementfunktionen für Input und Output:
open() := Öfffnet eine Datei für einen Stream
close() := Schliesst eine Datei für einen Stream
is_open() := Gibt zurück, ob eine Datei noch offen ist.
Elementfunktionen für Input:
tellg() := Gibt die Leseposition zurück
seekg() := Setzt die Leseposition
Elementfunktionen für Output:
tellp() := Gibt die Schreibposition zurück
seekp() := Setzt die Schreibposition
Elementfunktionen für Buffer-Klasse:
rdbuf() := Gibt einen Zeiger zum Stream-Buffer zurück
rdbuf(streambuf*) := Richtet einen Stream-Burfer ein, auf den das Argument zeigt, und gibt einen Zeiger auf den vorher benutzten Stream-Buffer zurück
Betrachten wir ein erstes Beispiel (aus [JOSUTTIS 1999]).
/* The following code example is taken from the book * "The C++ Standard Library - A Tutorial and Reference" * by Nicolai M. Josuttis, Addison-Wesley, 1999 * * (C) Copyright Nicolai M. Josuttis 1999. * Permission to copy, use, modify, sell and distribute this software * is granted provided this copyright notice appears in all copies. * This software is provided "as is" without express or implied * warranty, and with no claim as to its suitability for any purpose. */ #include <cstdlib> #include <iostream> using namespace std; int main() { double x, y; // operands // print header string cout << "Multiplication of two floating point values" << endl; // read first operand cout << "first operand: "; if (! (cin >> x)) { /* input error * => error message and exit program with error status */ cerr << "error while reading the first floating value" << endl; return EXIT_FAILURE; } // read second operand cout << "second operand: "; if (! (cin >> y)) { /* input error * => error message and exit program with error status */ cerr << "error while reading the second floating value" << endl; return EXIT_FAILURE; } // print operands and result cout << x << " times " << y << " equals " << x * y << endl; }
Include-Datei 'iostream' wird für Input- und Output mit Standardobjekten benutzt. Als solche werden 'cin', 'cout' und 'cerr' benutzt werden. Include-Datei 'cstdlib' wird für Fehlermeldungen benötigt wie dem Makro 'EXIT_FAILURE'.
In der Zeile
cout << "Multiplication of two floating point values" << endl;
wird mit cout ein String auf den Bildschirm ausgegeben und mittels 'endl' mit newline und flush beendet (für 'endl' siehe weiter unten ---> Manipulatoren).
Die folgende Zeile fragt indirekt den Zustand des Strems ab. Der Operator '! ()' wird wahr, wenn im Stream ein Fehler aufgetreten ist (für Fehlerzustände siehe unten ---> Stream-Zustände).
if (! (cin >> x)) {
Im folgenden wird dann eine Fehlerausgabe auf den Fehlerkanal gegeben.
cerr << "error while reading the first floating value" << endl;
Aufrufe des Programms auf der Kommandozeile erzeugen folgenden Output:
gerd@goedel:~/WEB-MATERIAL/fh/I-PROGR2/I-PROGR2-EX/EX8> ./io1 Multiplication of two floating point values first operand: 12 second operand: 5 12 times 5 equals 60
gerd@goedel:~/WEB-MATERIAL/fh/I-PROGR2/I-PROGR2-EX/EX8> ./io1 Multiplication of two floating point values first operand: -1.2 second operand: 5 -1.2 times 5 equals -6
gerd@goedel:~/WEB-MATERIAL/fh/I-PROGR2/I-PROGR2-EX/EX8> ./io1 Multiplication of two floating point values first operand: a error while reading the first floating value gerd@goedel:~/WEB-MATERIAL/fh/I-PROGR2/I-PROGR2-EX/EX8>
Was man 'nebenbei' an diesem Output sehen kann, ist, dass die Standardobjekte 'cin' und 'cout' mehrfach überladen sind. Sie sind für alle gängigen Datenformate 'voreingestellt'. Wie man eigene Datenformate zusätzlich einführen kann, das wird in einer Nachfolge-VL gezeigt werden.
Manipulatoren sind spezielle Objekte, um Streams zu manipulieren, so das bekannte 'endl', das schon mehrfach benutzt wurde:
std::cout << std::endl;
'endl' fügt bekanntlich ein '\n' ein. Zusätzlich wird der Output-Puffer geleert. Hier die Liste der Manipulatoren (noch nicht vollständig):
MANIPULATOR |
KLASSE |
BEDEUTUNG |
endl |
ostream |
Gibt '\n' aus und flushed den output-Puffer |
ends |
ostream |
Gibt '\0' aus |
flush |
ostream |
flushed den output-Puffer |
ws |
istream |
Liest whitespace und wirft es weg |
setiosflags(flags) |
ios_base |
Setzt flags als format-Flags (ruft setf(flags) für den Stream); benötigt Inklude-Datei <iomanip> (s.u. bei ---> Formatierung) |
setiosflags(mask) |
ios_base |
Klärt alle flags die durch mask identifiziert werden (Ruft setf(0,mask) für den Stream); benötigt Inklude-Datei <iomanip> (s.u. bei ---> Formatierung) |
boolalpha |
ios_base |
erzwingt alphanumerische Representation(s.u. bei ---> Formatierung) |
noboolalpha |
ios_base |
erzwingt numerische Representation(s.u. bei ---> Formatierung) |
setw(val) |
ios_base |
setzt die Feldbreite für input und output auf val (s.u. bei ---> Formatierung) |
setfill(c) |
ios_base |
definiert c als Füllzeichen (s.u. bei ---> Formatierung) |
left |
ios_base |
adjustiert den Wert links (s.u. bei ---> Formatierung) |
right |
ios_base |
adjustiert den Wert rechts (s.u. bei ---> Formatierung) |
internal |
ios_base |
adjustiert das Zeichen links und den Wert rechts (s.u. bei ---> Formatierung) |
oct |
ios_base |
schreibt und liest Oktalzahlen (s.u. bei ---> Formatierung) |
hex |
ios_base |
schreibt und liest Hexzahlen (s.u. bei ---> Formatierung) |
dec |
ios_base |
schreibt und liest Dezimalzahlen (s.u. bei ---> Formatierung) |
... |
ios_base |
... |
Im Rahmen der Klasse ios_base sind mehrere Zustandsbits definiert, anhand denen man den Zustand eines Streams ablesen kann. Sie stehen allen Objekten vom Typ basic_istream und basic_outstream zur Verfügung. Stream-Puffer haben allerdings keine Zustandsbits.
KONSTANTE |
BEDEUTUNG |
goodbit |
Alles ist OK; kein anderes Bit ist gesetzt |
eofbit |
Ende der Datei wurde erreicht |
failbit |
Fehler; eine I/O-Funktion war nicht erfolgreich |
badbit; |
Schlimmer Fehler; ein undefinierter Zustand wurde erreicht |
Diese Zustandsbits können mittels Funktionen der Klasse ios_base manipuliert werden.
good() := Gibt 'true' zurück wenn das goodbit gesetzt ist.
fail() := Gibt 'true' zurück wenn das failbit or badbit gesetzt ist.
bad() := Gibt 'true' zurück wenn das badbit gesetzt ist.
eof() := Gibt 'true' zurück wenn das eofbit gesetzt ist.
rdstate := Gibt die aktuell gesetzten Flags zurück
clear() := Klärt alle Flags
clear(state) := Klärt alle Flags und setzt state-Flags
setstate(state) := Setzt zusätzliche state-Flags
operator void* () := Gibt zurück, ob der Stream nicht in einen Fehler gelaufen ist
operator ! () := Gibt zurück, ob der Stream in einen Fehler gelaufen ist
exceptions(flags) := Setzt flags, die Ausnahmen triggern
exceptions() := Gibt die flags zurück, die Ausnahmen triggern
Aufgrund der Mängel der Ausnahmebehandlung in C++ wird im allgemeinen empfohlen, diese möglichst nicht zu benutzen
C++ bietet auch zahlreiche Möglichkeiten die Formate der ein- und Ausgaben zu beeinflussen. Um dies tun zu können, gibt es mehrere Möglichkeiten:
Format-Flags := jeder Stream besitzt eine Reihe von Flags, die besagen, welche Formateigenschaften aktiviert sind. diese kann man auslesen, kopieren und setzen.
Format-Manipulatoren := Format-Manipulatoren können im Rahmen der standard-Ein-Ausgabe Funktionen '<<' sowie '>>' benutzt werden, um direkt Formateigenschaften ein- oder auszuschalten (die Liste der format-Manipulatoren ist Teil der Liste der Manipulatoren; s.o.)
Format-Element-Funktionen := diese können benutzt werden, um unabhängig von einer aktuellen Ein- oder Ausgabeoperation formateigenschaften abzufragen oder zu setzen.
Einige der Format-Element-Funktionen sind hier aufgelistet.
FUNKTION |
BEDEUTUNG |
setf(flags) |
Setzt flags als zusätzliche format-flags und gibt die bisherigen Zustand aller Flags zurück. |
setf(flags, mask) |
Setzt flags als zusätzliche format-flags für die durch mask identifizierte Gruppe und gibt die bisherigen Flags zurück. |
unsetf(flags, mask) |
Klärt flags. |
flags() |
Gibt alle aktuell gesetzten Format-flags zurück. |
flags(flags) |
Setzt flags als die neuen Format-flags und gibt die bisherigen Flags zurück. |
copyfmt(stream) |
Kopiert alle Format-Definitionen von stream. |
copyfmt(stream) |
Kopiert alle Format-Definitionen von stream. |
Dateien können im Rahmen spezieller Datei-Streams bearbeitet werden. Neben den Standardfunktionen für Streams:
>> := Liest aus dem Stream
get() := liest das nächste Zeichen
getline() := liest ganze Zeile
read(count) := liest count-viele Zeichen
readsome(count) := liest bis zu count-viele Zeichen
gcount() := Gibt die Anzahl der gelesenen Zeichen durch die letzte read-Operation zurück
ignore() := ignoriert Zeichen
peek() := Liest das nächste Zeichen ohne es aus dem Stream zu entfernen
unget() := Setzt das letzte gelesene Zeichen wieder zurück in den Stream
putback() := wie unget()
Elementfunktionen für Output:
<< := Schreibt in den Stream
put(c) := Schreibt c in den Stream
write(count, str) := Schreibt count-viele Zeichen von str in den Stream
flush() := flushed die Puffer des Streams
stehen im Falle von Dateien auch noch spezielle Elementfunktionen für Input und Output zur Verfügung:
open() := Öfffnet eine Datei für einen Stream
close() := Schliesst eine Datei für einen Stream
is_open() := Gibt zurück, ob eine Datei noch offen ist.
Elementfunktionen für Input:
tellg() := Gibt die Leseposition zurück
seekg() := Setzt die Leseposition
Elementfunktionen für Output:
tellp() := Gibt die Schreibposition zurück
seekp() := Setzt die Schreibposition
Eng zusammenhängend mit Operationen mit Dateien sind die Elementfunktionen für die Buffer-Klasse:
rdbuf() := Gibt einen Zeiger zum Stream-Buffer zurück
rdbuf(streambuf*) := Richtet einen Stream-Burfer ein, auf den das Argument zeigt, und gibt einen Zeiger auf den vorher benutzten Stream-Buffer zurück
Im Folgenden ein erstes einfaches Beispiel, das später weiter ausgebaut werden soll.
/* The following code example is taken from the book * "The C++ Standard Library - A Tutorial and Reference" * by Nicolai M. Josuttis, Addison-Wesley, 1999 * * (C) Copyright Nicolai M. Josuttis 1999. * Permission to copy, use, modify, sell and distribute this software * is granted provided this copyright notice appears in all copies. * This software is provided "as is" without express or implied * warranty, and with no claim as to its suitability for any purpose. */ #include <string> // for strings #include <iostream> // for I/O #include <fstream> // for file I/O #include <iomanip> // for setw() #include <cstdlib> // for exit() using namespace std; // forward declarations void writeCharsetToFile (const string& filename); void outputFile (const string& filename); int main () { writeCharsetToFile("charset.out"); outputFile("charset.out"); } void writeCharsetToFile (const string& filename) { // open output file ofstream file(filename.c_str()); // file opened? if (! file) { // NO, abort program cerr << "can't open output file \"" << filename << "\"" << endl; exit(EXIT_FAILURE); } // write character set for (int i=32; i<256; i++) { file << "value: " << setw(3) << i << " " << "char: " << static_cast<char>(i) << endl; } } // closes file automatically void outputFile (const string& filename) { // open input file ifstream file(filename.c_str()); // file opened? if (! file) { // NO, abort program cerr << "can't open input file \"" << filename << "\"" << endl; exit(EXIT_FAILURE); } // copy file contents to cout char c; while (file.get(c)) { cout.put(c); } } // closes file automatically
Es wird ein Objekt mit Namen file vom Typ output-stream angelegt. Der Dateiname wird als ein C++-String übergeben, der mit der elementfunktion c_str() in einen C-String umgewandelt wird.
ofstream file(filename.c_str());
In der nachfolgenden Zeile wird wieder geprüft, ob ein Fehlerstatus bei Objekt file vorliegt.
if (! file) {
In der folgenden Zeile wird die Feldbreite der Ausgabe mit dem Manipulator setw(3) auf den Wert 3 gesetzt; zugleich wird der Eingabewert, der vom Typ int ist, explizit in einen Typ char 'gecastet':
file << "value: " << setw(3) << i << " " << "char: " << static_cast(i) << endl;
Ist ein File-Stream zum Lesen geöffnet, kann man ihn mittels einer Standardfunktion get() auslesen und zugleich auf den Bildschirm ausgeben. 'file.get(c)' liest das nächste item aus der Datei in die Variable c undcout.put(c) gibt es auf dem Bildschirm aus.
while (file.get(c)) { cout.put(c); }
Der Aufruf des Programms auf der Kommandozeile ergibt folgende Ausgabe:
gerd@goedel:~/WEB-MATERIAL/fh/I-PROGR2/I-PROGR2-EX/EX8> ./charset value: 32 char: value: 33 char: ! value: 34 char: " value: 35 char: # value: 36 char: $ value: 37 char: % value: 38 char: & value: 39 char: ' value: 40 char: ( value: 41 char: ) value: 42 char: * value: 43 char: + value: 44 char: , value: 45 char: - value: 46 char: . value: 47 char: / value: 48 char: 0 value: 49 char: 1 value: 50 char: 2 value: 51 char: 3 value: 52 char: 4 value: 53 char: 5 value: 54 char: 6 value: 55 char: 7 value: 56 char: 8 value: 57 char: 9 value: 58 char: : value: 59 char: ; value: 60 char: < value: 61 char: = value: 62 char: > value: 63 char: ? value: 64 char: @ value: 65 char: A value: 66 char: B value: 67 char: C value: 68 char: D value: 69 char: E value: 70 char: F value: 71 char: G value: 72 char: H value: 73 char: I value: 74 char: J value: 75 char: K value: 76 char: L value: 77 char: M value: 78 char: N value: 79 char: O value: 80 char: P value: 81 char: Q value: 82 char: R value: 83 char: S value: 84 char: T value: 85 char: U value: 86 char: V value: 87 char: W value: 88 char: X value: 89 char: Y value: 90 char: Z value: 91 char: [ value: 92 char: \ value: 93 char: ] value: 94 char: ^ value: 95 char: _ value: 96 char: ` value: 97 char: a value: 98 char: b value: 99 char: c value: 100 char: d value: 101 char: e value: 102 char: f value: 103 char: g value: 104 char: h value: 105 char: i value: 106 char: j value: 107 char: k value: 108 char: l value: 109 char: m value: 110 char: n value: 111 char: o value: 112 char: p value: 113 char: q value: 114 char: r value: 115 char: s value: 116 char: t value: 117 char: u value: 118 char: v value: 119 char: w value: 120 char: x value: 121 char: y value: 122 char: z value: 123 char: { value: 124 char: | value: 125 char: } value: 126 char: ~ value: 127 char: value: 128 char: value: 129 char: value: 130 char: value: 131 char: value: 132 char: value: 133 char: value: 134 char: value: 135 char: value: 136 char: value: 137 char: value: 138 char: value: 139 char: value: 140 char: value: 141 char: value: 142 char: value: 143 char: value: 144 char: value: 145 char: value: 146 char: value: 147 char: value: 148 char: value: 149 char: value: 150 char: value: 151 char: value: 152 char: value: 153 char: value: 154 char: value: 155 char: value: 156 char: value: 157 char: value: 158 char: value: 159 char: value: 160 char: value: 161 char: ¡ value: 162 char: ¢ value: 163 char: £ value: 164 char: ¤ value: 165 char: ¥ value: 166 char: ¦ value: 167 char: § value: 168 char: ¨ value: 169 char: © value: 170 char: ª value: 171 char: « value: 172 char: ¬ value: 173 char: value: 174 char: ® value: 175 char: ¯ value: 176 char: ° value: 177 char: ± value: 178 char: ² value: 179 char: ³ value: 180 char: ´ value: 181 char: µ value: 182 char: ¶ value: 183 char: · value: 184 char: ¸ value: 185 char: ¹ value: 186 char: º value: 187 char: » value: 188 char: ¼ value: 189 char: ½ value: 190 char: ¾ value: 191 char: ¿ value: 192 char: À value: 193 char: Á value: 194 char: Â value: 195 char: Ã value: 196 char: Ä value: 197 char: Å value: 198 char: Æ value: 199 char: Ç value: 200 char: È value: 201 char: É value: 202 char: Ê value: 203 char: Ë value: 204 char: Ì value: 205 char: Í value: 206 char: Î value: 207 char: Ï value: 208 char: Ð value: 209 char: Ñ value: 210 char: Ò value: 211 char: Ó value: 212 char: Ô value: 213 char: Õ value: 214 char: Ö value: 215 char: × value: 216 char: Ø value: 217 char: Ù value: 218 char: Ú value: 219 char: Û value: 220 char: Ü value: 221 char: Ý value: 222 char: Þ value: 223 char: ß value: 224 char: à value: 225 char: á value: 226 char: â value: 227 char: ã value: 228 char: ä value: 229 char: å value: 230 char: æ value: 231 char: ç value: 232 char: è value: 233 char: é value: 234 char: ê value: 235 char: ë value: 236 char: ì value: 237 char: í value: 238 char: î value: 239 char: ï value: 240 char: ð value: 241 char: ñ value: 242 char: ò value: 243 char: ó value: 244 char: ô value: 245 char: õ value: 246 char: ö value: 247 char: ÷ value: 248 char: ø value: 249 char: ù value: 250 char: ú value: 251 char: û value: 252 char: ü value: 253 char: ý value: 254 char: þ value: 255 char: ÿ
Zugleich wurde eine Datei 'charset.out' angelegt.
Schreiben Sie eine Funktion, mittels der ein Anwender einen beliebig langen Text über die Tastatur eingeben kann, der in einem String gespeichert wird.
Schreiben sie eine Funktion, die den Inhalt eines (Text-)Strings in eine Datei abspeichert. Der Dateiname kann vom Benutzer frei gewählt werden.
Schreiben sie eine Funktion, die eine Textdatei öffnet und den Inhalt der Datei in einen String einliest.
Schreiben Sie eine Funktion, mittels der ein Anwender einen beliebig langen Text über die Tastatur eingeben kann, der in einem String gespeichert wird. Besonderheit: der Text ist in Felder strukturiert, die aus Zeichenketten bestehen und aus Zahlen vom Typ double.
Schreiben sie eine Funktion, die eine Datei öffnet, die in Feldern strukturiert ist, die auch Zahlen vom Typ double enthalten können und die den Inhalt der Datei in Strings bzw. Zahlen abspeichert.
Schreiben sie eine Funktion, die den Inhalt eines (Text-)Strings an eine schon existierende Datei anhängt.
Öffne Sie eine Datei und suchen Sie in dieser Datei nach einem Muster M; wenn das Muster gefunden wird, geben sie die Position in der Datei an, ab der das Muster in der Datei auftritt.
Öffne Sie eine Datei und suchen Sie in dieser Datei nach einem Muster M; ersetzen Sie das Muster M in der Datei durch ein anderes Muster M'.