C++ Kurs

Eingabestream cin

Die Themen:

Eingabestream cin

Für die Standard-Eingabe, in der Regel ist dies die Tastatur, wird in C++ Programme vorzugsweise den Eingabestream cin verwendet.

Genauso wie der Ausgabestream cout, ist auch der Eingabestream cin eine Instanz einer Streamklasse und liegt im Namensraum std. Wenn dieser Stream verwendet wird, ist die Datei iostream einzubinden, welche auch schon für den Ausgabestream cout benötigt wurde.

Die allgemeine Syntax für die formatierte Eingabe mittels cin lautet:

std::cin >> var1 [ >> var2 [ >> var3 ...]];

Nach dem Eingabestream cin folgt der Operator >> und dann der Name der einzulesenden Variable. Mehrere einzulesende Variablen werden einfach durch entsprechende Wiederholungen aneinander gehängt. Die Datentypen der Variablen können dabei beliebig gemischt vorkommen. Es muss dann bei der Eingabe nur darauf geachtet werden, dass die eingelesenen Werte sich auch in den dazugehörigen Variablen abspeichern lassen. Was bei fehlerhaften Eingaben passiert, dass sehen wir uns gleich an.

PgmHeader
// Datei einbinden
#include <iostream>
using std::cin;

// Variablen definieren
short var1;
long var2;

// main() Funktion
int main ()
{
   ....
   // 2 Daten einlesen
   cin >> var1 >> var2;
   ....
}

Eingabe von numerischen Daten und Texten

Bei der Eingabe von mehreren Daten werden die einzelnen Daten durch Leerzeichen voneinander getrennt eingegeben. Die Zuordnung, Eingabe zur Variable, erfolgt von links nach rechts, d.h. die erste Eingabe wird auch der ersten Variable im cin-Stream zugewiesen (wenn kein Eingabefehler vorliegt).

Standardmäßig werden alle numerischen Eingaben im Dezimalformat erwartet. Die Eingabe kann jedoch mit den bekannten Manipulatoren dec, hex und oct auf eine andere Zahlenbasis umgestellt werden (siehe nachfolgendes Beispiel). Die eingestellten Zahlenbasen für cout und cin arbeiten unabhängig voneinander.

 

HinweisObwohl Feldern später noch in einem eigenen Kapitel behandelt werden, benötigen wir in diesem Kapitel char-Felder zum Abspeichern von alphanumerischen Eingaben. Ein char-Feld wird wie folgt definiert:

char myArray[SIZE];

myArray ist der Name des Feldes und SIZE die Feldgröße. In einem so definierten Feld können maximal SIZE-1 Zeichen abgelegt werden. Die -1 rührt daher, dass C-Strings immer mit einer binären 0 abgeschlossen werden, und die muss schließlich auch im Feld abgespeichert werden.

Das Einlesen von Texten erfordert besondere Sorgfalt. Da die Daten durch Leerzeichen getrennt werden, wird jedes einzelne Wort in einem eigenen char-Feld abgelegt, welches, wie bei C-Strings üblich, automatisch mit einer binären Null abgeschlossen wird. Soll einen Text, der aus mehreren Wörtern besteht eingelesen werden, so muss dies zeilenweise erfolgen (siehe nächsten Abschnitt).

PgmHeader
// Einlesen eines Hex-Wertes
short var;
cin >> hex >> var;

// Einlesen eines Oktal-Wertes
short var;
cin >> oct >> var;

// Einlesen zweier Wörter (mit je max. 9 Buchstaben!)
char array1[10], array2[10];
cin >> array1 >> array2;
AchtungDie oben dargestellte Eingabe ist zwar syntaktisch richtig und wird auch funktionieren. Jedoch wird das Verhalten des Programms undefiniert, wenn ein Wort mit mehr als 9 Zeichen eingegeben wird (9 Zeichen wegen der abschließenden 0!). Wie's sicherer geht erfahren Sie gleich noch.

Einlesen von Zeilen

Da beim Eingabestream cin die einzelnen Eingaben durch Leerzeichen voneinander getrennt werden, können Eingaben, die selbst Leerzeichen enthalten, nur zeilenweise eingelesen werden. Um eine komplette Zeile einzulesen, wird die cin-Memberfunktion getline(...) verwendet werden. getline(...) besitzt folgende Syntax:

istream& getline (char *pBuffer, int size);

getline(...) erhält im ersten Parameter die Adresse des char-Feldes übergeben, in dem die Eingabe abgelegt werden soll. Der zweite Parameter gibt den für die Ablage der Eingabe zur Verfügung stehenden Speicher an. Da die Eingabe als C-String abgelegt wird, welcher mit einer 0 abschließt, werden maximal size-1 Zeichen im Puffer abgelegt. Und selbstverständlich sollte das im ersten Parameter angegebene Feld auch groß genug sein, um die Eingabe aufnehmen zu können.

Nachfolgend noch ein Beispiel zu getline(...). Zunächst wird ein char-Feld mit der Größe SIZE (gleich 81) definiert, in dem die eingegebene Zeile abgelegt werden soll. Damit darf die Eingabe aus maximal SIZE-1 Zeichen bestehen. Werden mehr als SIZE-1 Zeichen eingegeben, so werden nur SIZE Zeichen im char-Feld abgelegt. Die restlichen eingegebenen Zeichen der Eingabezeile verbleiben zunächst im Eingabepuffer. Da aber in diesem Fall SIZE Zeichen übernommen wurden, nun jetzt natürlich die abschließende binäre 0, die ja für den Abschluss eines C-Strings erforderlich ist. Diese binäre 0 wird im Beispiel zur Sicherheit immer als letzte Zeichen im char-Feld eingetragen, damit der String auf jeden Fall abgeschlossen ist. Anschließend wird die restliche Eingabe im Eingabepuffer mittels cin.ignore(...) verworfen. Zum Schluss wird der Inhalt des char-Felds dann zur Kontrolle ausgegeben.

HinweisDie cin-Memberfunktion ignore(...) erhält im ersten Parameter die maximale Anzahl der zu überspringenden Zeichen und im zweiten Parameter ein Abbruchzeichen. Wird vor Erreichen der im ersten Parameter angegebenen Anzahl von Zeichen das Abbruchzeichen gefunden, verbleiben die restlichen Zeichen im Eingabepuffer und können von der nächsten cin-Anweisung verarbeitet werden. Im Beispiel wird die maximale Anzahl der zu verwerfenden Zeichen auf die Größe des Eingabepuffers gesetzt und als Abbruchzeichen der Zeilenvorschub (RETURN) verwendet.
PgmHeader
#include <iostream>
#include <limits>
using namespace std;

// Konstante für Feldgrösse definieren
constexpr int SIZE = 81;

// main() Funktion
int main ()
{
   // Feld zur Aufnahme der Eingabe definieren
   char array[SIZE];

   // Hinweis ausgeben
   cout << "Bitte einen Text eingeben: ";
   // Zeile einlesen, aber max. SIZE Zeichen
   cin.getline(array, SIZE);
   // Zur Sicherheit 0 anfuegen
   array[SIZE-1] = 0;
   // Restliche Eingabe verwerfen
   cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

   // Eingabe wieder ausgeben
   cout << "Eingabe war: " << array << endl;
}
Programmausgabe Bitte einen Text eingeben: Ein Text mit Leerzeichen!
 Eingabe war: Ein Text mit Leerzeichen!

 getline(...) interpretiert alle Eingaben als Text. Wenn also

12 2<RETURN>

eingegeben wird, so liegen die Daten als C-String "12 2" vor. Wie diese 'String-Werte' dann doch noch in numerische Werte konvertieren werden können, das folgt gleich.

Die genaue Funktionsdeklaration der cin-Memberfunktion getline(...) und was die Memberfunktion noch so alles zu leisten vermag, das erfahren Sie wenn Sie links das Symbol anklicken.
Achtung

Verwenden Sie innerhalb eines Programms die formatierte Eingabe (cin >> ...) und die unformatierte Eingabe (cin.getline(...)), so beachten Sie unbedingt die nachfolgenden Ausführungen!

Sehen Sie sich einmal das nachfolgende Beispiel an. Dort wird zunächst ein Text (formatiert) in das Feld array eingelesen. Anschließend soll nun eine komplette Zeile (unformatiert) eingelesen werden. Wenn Sie dieses Code-Stückchen so laufen lassen würden, so wird das unformatierte Einlesen der Zeile fehlschlagen!

PgmHeader
char array[80];
...
// Formatierte Eingabe
cin >> array;
...
// Unformatierte Eingabe
cin.getline(array,sizeof(array));

Was passiert hier? Überlegen Sie sich einmal, welche Tasten Sie bei der ersten, formatierten Eingabe gedrückt haben. Nun?

Die zuletzt gedrückte Taste war die <RETURN>-Taste, d.h. im Eingabepuffer liegen der eingegebene Text und das abschließende RETURN. Bei der formatierten Eingabe wird nun nur der Text ohne das RETURN aus dem Eingabepuffer geholt, d.h. das RETURN bleibt noch im Eingabepuffer liegen. Wird dann unformatiert mit getline(...) eingelesen, so wird zunächst dieses RETURN gefunden und damit ist für getline(...) die Sache erledigt! Deshalb gilt: verwenden Sie in einem Programm sowohl die formatierte als auch und unformatierte Eingabe, leeren Sie vor einen Wechsel stets den Eingabepuffer mit cin.ignore(...), so wie weiter oben angegeben.

Fehlerfälle

Soll mittels cin ein numerischer Wert eingelesen werden und es wird stattdessen ein nicht-numerischer Ausdruck eingegeben, so behält die einzulesende Variable ihren ursprünglichen Wert. Die Eingabe wird dann an die nächsten cin Anweisung übergeben, welche auch nicht-numerische Eingaben verarbeiten kann (siehe erste Eingabe im folgenden Beispiel). Dieser Fehlerfall kann abfangen werden, indem nach dem Einlesen die cin-Memberfunktion fail() aufgerufen wird. Liefert die Memberfunktion true zurück, so war die Eingabe fehlerhaft. In diesem Fall ist aber unbedingt noch zu beachten, dass der Fehlerstatus des Eingabestreams cin mittels clear() gelöscht werden muss und dass die Eingabe noch im Eingabepuffer steht!

Besteht die Eingabe eines numerischen Wertes am Anfang aus numerischen Zeichen gefolgt von nicht-numerischen Zeichen, so erhält die einzulesende Variable den numerischen Teil der Eingabe zugewiesen. Der Rest der Eingabe wird dann wiederum an die nächste cin Anweisung weitergeleitet, die den nicht-numerischen Teil der Eingabe verarbeiten kann (siehe zweite Eingabe im Beispiel). Unglücklicherweise liefert die Memberfunktion fail() hier false zurück, da ja die Eingabe (wenigstens teilweise) erfolgreich war.

PgmHeader
short var;
cin >> var;
// Wenn fail() true zurückgibt
if (cin.fail())
{
   // Fehler löschen und Eingabe ueberspringen
   cin.clear();
   cin.ignore(std::numeric_limits<int>::max(),'\n');
}

Eingabe : MaxMaier
Ergebnis: var behält ihren ursprünglichen Inhalt und fail() liefert true zurück

Eingabe : 12MaxMaier
Ergebnis: var erhält den Wert 12. fail() liefert false!

Soll sichergestellt werden, dass nach jeder Eingabe der Eingabepuffer vollständig leer ist, so müssen die eventuell im Eingabepuffer noch befindlichen Zeichen mittels der Memberfunktion ignore(...) verworfen werden.

Beispiel und ÜbungUnd hier geht's zum Beispiel und zur Übung.