C++ Kurs

Eingabestream cin

Die Themen:

Eingabestream cin
Eingabe von numerischen Daten und Texten
Einlesen von Zeilen
Fehlerfälle
Beispiel und Übung

Eingabestream cin

Für die Standard-Eingabe wird in C++ Programme vorzugsweise den Eingabestream cin verwendet. Die Standard-Eingabe erfolgt in der Regel über die Tastatur.

Genauso wie der Ausgabestream cout, ist auch der Eingabestream cin eine Instanz einer Streamklasse und liegt im Namensraum std. Wenn Sie diesen Stream einsetzen, müssen Sie die Datei iostream einbinden, die Sie auch schon für den Ausgabestream cout benötigt haben.

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 die Namen einzulesende Variable. Mehrere einzulesende Variablen werden einfach durch entsprechende Wiederholungen aneinander gehängt. Die Datentypen der Variablen können dabei beliebig 'gemischt' sein. Sie müssen dann bei der Eingabe 'nur' darauf achten, dass die eingelesenen Werte sich in den dazugehörigen Variablen abspeichern lassen. Was bei fehlerhaften Eingaben passiert, dass erfahren Sie gleich noch.


// 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 zugewiesen (wenn kein Eingabefehler vorliegt).

Obwohl Feldern später noch in einer eigenen Lektion behandelt werden, benötigen wir in dieser Lektion 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.

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

Beim Einlesen von Texten müssen Sie Obacht geben. 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. Wollen Sie einen Text der aus mehreren Wörtern besteht einlesen, so müssen Sie dies zeilenweise tun (siehe nächsten Abschnitt).


// 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;

Die oben dargestellte Eingabe ist zwar syntaktisch richtig und wird auch funktionieren. Jedoch wird das Verhalten des Programms undefiniert, wenn ein Wort mehr als 9 Zeichen besitzt (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 nicht direkt eingelesen werden. Soll eine komplette Zeile eingelesen werden, so kann hierfür die cin-Memberfunktion getline(...) verwendet werden. getline(...) besitzt prinzipiell folgende Deklaration:

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

getline(...) erhält im ersten Parameter die Adresse des char-Feldes übergeben, in dem die Eingabe abgelegt werden soll. Der zweite Parameter definiert die maximale Anzahl der einzulesenden Zeichen plus 1. Wenn Sie z.B. maximal 10 Zeichen einlesen wollen, so müssen Sie hier den Wert 11 übergeben (und selbstverständlich auch das char-Feld mit mindestens 11 Elementen definieren!). Der Grund für dieses Verhalten ist, dass getline(...) die Eingabe ebenfalls als C-String ablegt, und der wird bekanntermaßen mit einer binären 0 abgeschlossen (was man nicht oft genug wiederholen kann!).

Und zu getline(...) noch ein Beispiel. Zunächst wird ein char-Feld mit der Größe SIZE (gleich 81) definiert, in das eine komplette Zeile eingelesen werden soll. Damit können Sie SIZE-1 Zeichen einlesen, die dann als String im char-Feld abgelegt werden. Werden mehr als SIZE-1 Zeichen eingegeben, so werden SIZE Zeichen im char-Feld abgelegt. Die restlichen eingegebenen Zeichen verbleiben zunächst im Eingabepuffer. Da aber SIZE Zeichen übernommen wurden, fehlt jetzt natürlich die abschließende binäre 0, die ja für den Abschluss eines 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(...) übersprungen. Zum Schluss wird das char-Feld dann mittels cout zur Kontrolle ausgegeben.

Die 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 angegeben 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 Abbruchzeichen auf Zeilenvorschub (RETURN).



#include <iostream>
#include <limits>
using namespace std;

// Konstante für Feldgrösse definieren
const 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;
}

Bitte einen Text eingeben: Ein Text mit Leerzeichen!
Eingabe war: Ein Text mit Leerzeichen!

Beachten Sie beim Einlesen mittels getline(...), dass alle Eingaben als ASCII-Zeichen im char-Feld abgelegt werden. Wenn Sie also

12 2<RETURN>

eingeben, so liegen die 'Werte' als String "12 2" vor. Wie Sie diese 'String-Werte' in numerische Werte konvertieren können, das erfahren Sie noch.

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.


Verwenden Sie innerhalb eines Programms die formatierte Eingabe (cin >> ...) und die unformatierte Eingabe (cin.getline(...)), so beachten Sie unbedingt die folgenden 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 laufen lassen würden, so würde das unformatierte Einlesen der Zeile fehlschlagen!


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 natürlich die <RETURN>-Taste, d.h. im Eingabepuffer liegt 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 findet es zunächst das RETURN und damit ist für getline(...) die Sache erledigt! Deshalb gilt: verwenden Sie in einem Programm sowohl die formatierte wie 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 der nächsten cin Anweisung 'zugewiesen', die alphanumerische Eingaben verarbeitet (siehe erste Eingabe im folgenden Beispiel). Diesen Fehlerfall können Sie abfangen, indem Sie nach dem Einlesen die Memberfunktion fail() aufrufen. Liefert die Memberfunktion true zurück, so war die Eingabe fehlerhaft. Beachten Sie aber, dass Sie danach unbedingt den Fehlerstatus des Eingabestreams cin mittels clear() löschen müssen 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 alphanumerische Eingaben verarbeitet (siehe zweite Eingabe im Beispiel). Unglücklicherweise liefert die Memberfunktion fail() hier false zurück, da ja die Eingabe (wenigstens teilweise) erfolgreich war.


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!

Wollen Sie sicher sein, dass nach jeder Eingabe der Eingabepuffer vollständig leer ist, so müssen Sie die im Eingabepuffer noch befindlichen Zeichen wieder mittels der Memberfunktion ignore(...) verwerfen.

Beispiel und Übung Übung

Beispiel:

Das nachfolgende Beispiel zeigt, dass Sie nun schon in der Lage sind, auch komplizierter Berechnungen durchzuführen.

Das Beispiel dient zur Lösung eines Gleichungssystems mit 2 Unbekannten:

A1x + B1y + C1 = 0
A2x + B2y + C2 = 0

Das Gleichungssystem wird durch Gleichsetzen der beiden Gleichungen gelöst. Dazu werden beiden Gleichungen zuerst so umgeformt, dass auf der linken Seite der Gleichungen nur noch x übrig bleibt. Die beiden umgeformten Gleichungen werden gleichgesetzt und daraus dann y berechnet. Der so erhaltene y-Wert wird dann in eine der beiden Gleichungen eingesetzt und daraus der x-Wert berechnet (siehe auch Kommentar im Code).

Dieses Programm löst ein Gleichungssystem mit zwei Unbekannten:
     A1x + B1y + C1 = 0
     A2x + B2y + C2 = 0
Alle Koeffizienten müssen ungleich 0 sein!
Bitte Koeffizienten der Gleichungen als Gleitkommazahlen!
(A1 B1 C1)? 1.0 -2.0 -4.0
(A2 B2 C2)? 2.0 5.0 -35.0
Ergebnis: X = 10, Y = 3


// Beispiel zu cin
// Zuerst Dateien iostream und iomanip einbinden

#include <iostream>
#include <iomanip>
using std::cout;
using std::cin;
using std::endl;

// Variablen für 1. Gleichung definieren
double a1, b1, c1;
// Variablen für 2. Gleichung definieren
double a2, b2, c2;
// Normierte Koeffizienten
double b1Norm, c1Norm; double b2Norm, c2Norm;
// Lösung der Gleichung
double x, y;

// main() Funktion
int main ()
{
   // Hinweis auf Programmfunktion ausgeben
   cout << "Dieses Programm löst ein Gleichungssystem mit zwei Unbekannten:\n"
        << "\tA1x + B1y + C1 = 0\n\tA2x + B2y + C2 = 0\n"
        << "Alle Koeffizienten müssen ungleich 0 sein!\n";
   cout << "Bitte Koeffizienten der Gleichungen als Gleitkommazahlen!\n"
        << "(A1 B1 C1)? ";
   // Koeffizienten der 1. Gleichung einlesen
   cin >> a1 >> b1 >> c1;
   cout << "(A2 B2 C2)? ";
   // Koeffizienten der 2. Gleichung einlesen
   cin >> a2 >> b2 >> c2;

   // Koeffizienten normieren und Gleichung nach x auflösen
   // x = -B1Ny - C1N
   // x = -B2Ny - C2N

   b1Norm = -b1 / a1;
   c1Norm = -c1 / a1;
   b2Norm = -b2 / a2;
   c2Norm = -c2 / a2;
   // Durch Gleichsetzen der Gleichungen nun Y ausrechnen
   //     -B1Ny - C1N = -B2N - C2N
   //     y = (C2N-C1N) / (B1N - B2N)

   y = (c2Norm-c1Norm)/(b1Norm-b2Norm);
   // X nun ausrechnen durch einsetzen des Y-Wertes in 1. Gleichung
   x = (-b1*y - c1)/a1;
   // Und Ergebnis ausgeben
   cout << "Ergebnis: X = " << x << ", Y = " << y << endl;
}

Übung:

Und jetzt mal etwas aus dem alltäglichen Leben.

Sie sollen in der Übung den Steueranteil und den Nettobetrag Ihres Einkaufs ausrechnen. Dazu werden der an der Kasse bezahlte Betrag und der darin enthaltene Steuersatz in % eingegeben. Das Programm soll dann berechnen, wie viel Steuer im gezahlten Betrag enthalten ist und wie viel Sie ohne Steuer hätten zahlen müssen.

Hinweis: Bei einem angenommenen Steuersatz von 7% zahlen Sie 107% an der Kasse, nämlich 100% für Ihre Ware und 7% für das Finanzamt.

Die berechneten Beträge sind mit 2 Nachkommastellen auszugeben

Wie viel EUR haben Sie bezahlt? 233.45
Und wie hoch ist die Steuer (%)? 7.
Steueranteil: 15.27
Nettobetrag : 218.18

Lösung ansehen!