namespace cpp

C++ lernen, kennen, anwenden

Benutzer-Werkzeuge

Webseiten-Werkzeuge


kennen:include:iostream

<iostream>

Die Verwendung von Flags zur Kontrolle eines Stromzustands
ist jedenfalls eher eine Studie über Implementierungstechniken
als über Schnittstellen-Design.
— Bjarne Stroustrup [C++ 3rd edn, 21.4.1]</file>

Ein- und Ausgabeströme

C++ bietet eine einheitliche Schnittstelle für ein- und ausgehende Datenströme. Dabei ist es gleichgültig, woher die Daten kommen (std::istream) oder wohin sie gehen (std::ostream), ob Geräte (Konsole), Dateien oder Zeichenketten. Die Kontrolle des Stromzustands, Positionierung bei wahlfreiem Zugriff und Formatierung werden einheitlich gehandhabt.

Eingabe Ausgabe beides
allgemein std::istream std::ostream std::iostream
Dateien std::ifstream std::ofstream std::fstream
Zeichenketten std::istringstream std::ostringstream std::stringstream

Stromtypen für wchar_t-Zeichen wird noch jeweils ein w vorangestellt.

Standardkanäle sind während der Programmlaufzeit mit Stromvariablen verbunden:

std::cin Eingabe-Konsole (Tastatur)
std::cout Ausgabe-Konsole (Bildschirm)
std::cerr Fehlerausgabe ungepuffert
std::clog Fehlerausgabe gepuffert

Ausgabe

Ausgabeströme std::ostream nehmen Daten aller Typen entgegen, für die der Ausgabeoperator « überladen ist, und formatieren deren Ausgabe als Folge von Zeichen. Sie schreiben unformatierte Zeichen und Rohspeicher, besonders für Binärdateien.

void ausgabe(std::ostream& out, double d)
{ 
  out << d;                           // formatierte Ausgabe
  out.put(' ');                       // Zeichenausgabe
  out.write( (char*) &d, sizeof(d) ); // Rohspeicher 
}

Eingabe

Eingabeströme std::istream wandeln eingegebene (zulässige) Zeichenfolgen in Werte jener Typen, für die der Eingabeoperator » überladen ist. Führende Leerzeichen werden vom Eingabeoperator » stets verschluckt. Darüber hinaus lassen sich Einzelzeichen und Rohspeicher, meist aus Binärdateien, unformatiert einlesen.

void eingabe(std::istream& in, double& d)
{
  in >> d;                          // formatierte Eingabe 
  char c = in.get();                // Einzelzeichen, auch ' ' 
  in.get(c);                        // dto.
  in.putback(c);                    // zurückschieben 
  c = cin.peek();                   // nachschauen, aber nicht einlesen
  in.read( (char*) &d, sizeof(d) ); // Rohspeicher
}

Das Einlesen von Zeichenketten verlangt besondere Sorgfalt. Führende Leerzeichen werden übergangen, das Einlesen endet beim ersten whitespace danach.

char str[10]; // lang genug?
cin >> str;   // kann und wird schief gehen

Die Methoden get() und getline() lesen auch führende whitespaces in die Zeichenkette ein, begrenzen deren Länge auf maximal n-1 Zeichen und hängen stets eine '\0' an. Werden keine oder n-1 Zeichen vor dem Erreichen des ende-Zeichens gelesen, wird das failbit gesetzt (siehe Stromzustand).

int zeichenketteneingabe(std::istream& in, char* s, int n, char ende='\n')
{
  in.getline(s, n, ende); // ende wird verschluckt
  in.getline(s, n);       // ende am Zeilenumbruch '\n'
  in.get(s, n, ende);     // ende wird nicht entfernt
  in.get(s, n);           // geht schief, wenn schon bei '\n'
  return in.gcount();     // Anzahl zuletzt gelesener Zeichen
}

Stromzustand

Der Stromzustand von Ein- und Ausgabeströmen ist erfragbar. Er wird intern durch Flags goodbit, badbit, failbit und eofbit aus der Basisklasse std::ios_base repräsentiert. Er kann mit rdstate() abgefragt werden. Während clear(state) den Zustand komplett überschreibt, setzt setstate(state) einzelne Bits zusätzlich.

std::ios_base::iostate zustand(std::istream& s)
{
  if (s)        cout << "ok ";
  if (s.good()) cout << "gut ";
  if (s.eof() ) cout << "noch gut, aber am Dateiende ";
  if (s.fail()) cout << "Aktion schiefgegangen, "
                        "Strom noch verwendbar ";
  if (s.bad() ) cout << "Strom total durcheinander ";
  return s.rdstate();
}

Ein guter Stromzustand ist Voraussetzung, aber keine Garantie für den Erfolg weiterer Aktionen mit dem Strom. Bei nicht gutem Zustand haben Ein- und Ausgaben keine Wirkung.

In der Voreinstellung werfen die Ströme keine Ausnahmen. Mit der Methode exceptions(flags) können sie jedoch dazu gebracht werden, bei Fehlern eine von system_error std::ios_base::failure-Ausnahme zu werfen:

void lass_es_krachen(std::istream& in)
{
  std::ios_base::iostate old = in.exceptions(); // goodbit bedeutet: keine Ausnahmen werfen
  try
  {
    in.exception(std::ios_base::failbit); // wirft Ausnahme sofort, falls failbit schon gesetzt ist
    int x;
    in >> x; // wirft Ausnahme, wenn keine Zahl im Strom ist
  }
  catch(std::ios_base::failure& e)
  {
    std::cerr << "Fehler in Eingabe: " << e.what() << " Fehlercode: " << e.code() << '\n';
  }
  in.exceptions(old);
}

Ein- und Ausgabe selbstdefinierter Typen sollten so definiert werden, dass Variablen beim Einlesen nicht in einen undefinierten Zustand geraten können und der einzulesende Wert erst dann geändert wird, wenn die Eingabe erfolgreich war.

struct Punkt { int x, y; };
 
std::istream& operator>>(std::istream& is, Punkt& p)
{
  int x, y;
  if (is >> x >> y)
  {
    p.x = x; 
    p.y = y;
  }
  return is;
}

Positionierung und wahlfreier Zugriff

Ströme mit wahlfreiem Zugriff können die Lage der aktuellen Lese- und Schreibposition im Strom erfragen und ändern. Das ist vor allem für Binärdateien wichtig. Die Position kann absolut oder relativ zu Anfang std::ios_base::beg, aktueller Position std::ios_base::cur oder Ende der Datei std::ios_base::end gesetzt werden.

long filesize(std::istream& is)
{
  long pos = is.tellg();           // alte Position merken   
  is.seekg(0, std::ios_base::beg); // zum Anfang
 
  long beg = is.tellg();      
  is.seekg(0, std::ios_base::end); // zum Ende
 
  long end = is.tellg();
  is.seekg(pos);                   // alte Position wiederherstellen 
 
  return end-beg;
}

Bei Ausgabeströmen arbeiten tellp() und seekp() sinngemäß.

Formatierung

Das Verhalten der Eingabeströme und das Aussehen der Ausgaben (Formatierung) wird durch Schalter (Flags) in der Basisklasse std::ios_base gesteuert.

void formatierung(std::istream& in)
{
  long alt = in.flags();       // abfragen
  long neu = ios_base::skipws; // führende Leerzeichen verschlucken
  in.flags(alt | neu);         // Schalter setzen
  in.setf(neu);                // Flags setzen
  in.unsetf(neu);              // Flags zurücksetzen  
}

Ganzzahlen sind in drei Positionssystemen (Basis, 8, 10, 16) darstellbar. Programmierer verwechseln immer Weihnachten mit Helloween, weil OCT 31 = DEC 25:

void ganzzahlbasis(std::ostream& out)
{
  int i = 25;
  out.setf(std::ios_base::hex, std::ios_base::basefield);
  out << i << ' ';
  out.setf(std::ios_base::oct, std::ios_base::basefield);
  out << i << ' ';
  out.setf(std::ios_base::dec, std::ios_base::basefield);
  out << i << endl;  // Ausgabe: 19 31 25
}                      

Die Methode setf(flag, mask) wirkt wie flags(flags()&~mask | flag&mask). Dadurch werden alle Flags in mask zurückgestellt und flag neu gesetzt. Dies verhindert, dass zwei einander widersprechende Schalter gleichzeitig gesetzt werden.

Folgende Schalterkonstanten aus std::ios_base beeinflussen die Formatierung:

left internal right Ausrichtung bei vorgegebener Ausgabeweite in adjustfield
dec hex oct Ganzzahlbasis für Ein- / Ausgabe in basefield
showbase Ganzzahlbasis bei Ausgabe anzeigen: 0x19 031 25
fixed scientific Fließkomma als Festkomma / mit Exponent ausgeben in floatfield
showpoint Dezimalpunkt und Nachkommanullen bis zu festgelegter Genauigkeit anzeigen
showpos positives Vorzeichen immer anzeigen
uppercase Ausgabe X und E als Großbuchstabe
boolalpha Ausgabe von Wahrheitswerten als Wort entsprechend Locale
unitbuf nach jeder Ausgabeoperation Puffer leeren
skipws führende Leerzeichen bei Eingabe mit » verschlucken

Gleitkommazahlen zeigen den Dezimalpunkt und folgende Nullen nur, wenn showpoint gesetzt ist.

Die Ausgabegenauigkeit kann mit der Methode precision(n) gesteuert werden. Mit der Standardeinstellung 0 werden bis zu 6 Stellen angezeigt. Im fixed-Format ist n die Zahl der Nachkommastellen, im scientific-Format die Gesamtzahl der zählenden Stellen.

void genauer(std::ostream& out)
{
  int n = out.precision();
  out.precision(2*n);
}

Die Ausgabe eines Wertes nimmt immer mindestens soviele Zeichen ein, wie zur exakten Ausgabe notwendig sind. Die Ausgabeweite kann für die unmittelbar nächste Ausgabe vergrößert werden.

out.width(8);

Ist die befohlene Breite größer als notwendig, wird die Ausgabezeichenfolge an den Rändern ausgerichtet und mit Füllzeichen (Standardwert ' ') aufgefüllt. Nach jeder Ausgabe wird die Weite auf den Standardwert 0 zurückgesetzt, alle anderen Einstellungen sind bleibend.

-1.23___  // left (Standard), width(8)
-___1.23  // internal
___-1.23  // right

Das Füllzeichen lässt sich abfragen und ändern:

char c = out.fill();
out.fill('_');

Formatierung mit Manipulatoren

Manipulatoren bieten elegantere Schreibweisen zur Formatsteuerung:

int manipuliere(std::ostream& out)
{
  int i = 123;
  double d = 123.456789;
  std::cout << std::hex << i << ' '
            << std::dec << std::showpos << i << '\n';
  std::cout << std::setw(12) << std::right << i << '\n';
  std::cout << std::setw(12) << std::internal << i << '\n';
  std::cout << std::setw(12) << std::setprecision(3) << d << '\n';
  std::cout << std::setw(12) << std::fixed << d << '\n';
  std::cout << std::setw(12) << std::scientific << d << '\n';
  std::cout << std::setw(12) << std::hexfloat << d << '\n';      // C++11
  std::cout << std::setw(12) << std::defaultfloat << d << '\n';  // C++11
}

Es gibt folgende parameterlose Manipulatoren:

left internal right Ausrichtung breiter Ausgaben
dec hex oct Wechsel der Zahlenbasis bei Ein-/Ausgabe
showbase noshowbase Ganzzahlbasis 0… oder 0x… ausgeben
defaultfloat fixed hexfloat scientific Fließkommaausgabe normal / als Festkommazahl / mit Exponent / mit hexadezimaler Mantisse
showpoint noshowpoint Dezimalpunkt und folgende Nullen ausgeben
showpos noshowpos positives Vorzeichen ausgeben
uppercase nouppercase Groß-/klein-Ausgabe von X und E
boolalpha noboolalpha Wahrheitswerte als Wort / Zahl
endl ends gibt '\n' bzw. '\0' aus und leert den Puffer
flush leert den Ausgabepuffer
skipws noskipws Leerzeichen vor Eingabe verschlucken
ws verschluckt whitespaces der Eingabe

Manipulatoren mit Parametern sind in <iomanip> definiert:

setbase(base) Wechsel der Ganzzahlbasis (8, 10, 16)
setprecision(n) Genauigkeit von Fließkommazahlen
setw(width) Ausgabebreite
setfill© Füllzeichen festlegen
setiosflags(f) Setzen und
resetiosflags(f) Rücksetzen von Flags
quoted(s) Ein-/Ausgabe von std::string s mit Leerzeichen in "Gänsefüßchen" (C++14)
kennen/include/iostream.txt · Zuletzt geändert: 2017-03-24 10:11 (Externe Bearbeitung)