Warum sollten wir nach dem Lesen der Eingabe cin.clear () und cin.ignore () aufrufen?


85

Das C ++ - Tutorial der Google Code University enthielt diesen Code:

// Description: Illustrate the use of cin to get input
// and how to recover from errors.

#include <iostream>
using namespace std;

int main()
{
  int input_var = 0;
  // Enter the do while loop and stay there until either
  // a non-numeric is entered, or -1 is entered.  Note that
  // cin will accept any integer, 4, 40, 400, etc.
  do {
    cout << "Enter a number (-1 = quit): ";
    // The following line accepts input from the keyboard into
    // variable input_var.
    // cin returns false if an input operation fails, that is, if
    // something other than an int (the type of input_var) is entered.
    if (!(cin >> input_var)) {
      cout << "Please enter numbers only." << endl;
      cin.clear();
      cin.ignore(10000,'\n');
    }
    if (input_var != -1) {
      cout << "You entered " << input_var << endl;
    }
  }
  while (input_var != -1);
  cout << "All done." << endl;

  return 0;
}

Welche Bedeutung hat cin.clear()und cin.ignore()? Warum sind die 10000und \nParameter notwendig?

Antworten:


100

Das cin.clear()löscht das Fehlerflag ein cin(damit zukünftige E / A-Vorgänge ordnungsgemäß funktionieren) und cin.ignore(10000, '\n')springt dann zur nächsten neuen Zeile (um alles andere in derselben Zeile wie die Nicht-Nummer zu ignorieren, damit es keinen weiteren Analysefehler verursacht). . Es werden nur bis zu 10000 Zeichen übersprungen, sodass der Code davon ausgeht, dass der Benutzer keine sehr lange, ungültige Zeile einfügt.


55
+1. Ich möchte hinzufügen, dass es besser ist, anstatt bis zu 10000 Zeichen zu ignorieren, diese zu verwenden cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');.
Dennis

29
Wenn Sie verwenden möchten std::numeric_limits, stellen Sie sicher, dass #include <limits>.
Gbmhunter

1
Kann jemand die Syntax erklären std::numeric_limits<std::streamsize>::max()?
Minh Tran

4
@Minh Tran: std :: Streamsize ist ein vorzeichenbehaftetes Integral, das die Anzahl der übertragenen E / A-Zeichen oder die Größe des E / A-Puffers angibt. Hier wollten wir mithilfe der Vorlagenklasse "numeric_limits" die maximale Grenze des übertragenen E / A-Puffers oder Zeichens ermitteln.
Pankaj

1
@Minh Tran: Nur eine kleine Korrektur "Streamsize" ist keine Klasse, sondern nur ein integraler Typ. Wir können auch das Limit anderer erreichen, dh. int, char etc .. bitte überprüfen Sie es unter [Link] ( en.cppreference.com/w/cpp/types/numeric_limits )
Pankaj

44

Sie geben die

if (!(cin >> input_var))

Anweisung, wenn beim Übernehmen der Eingabe von cin ein Fehler auftritt. Wenn ein Fehler auftritt, wird ein Fehlerflag gesetzt und zukünftige Versuche, Eingaben zu erhalten, schlagen fehl. Deshalb brauchst du

cin.clear();

um das Fehlerflag loszuwerden. Außerdem befindet sich die fehlgeschlagene Eingabe in einer Art Puffer. Wenn Sie versuchen, erneut eine Eingabe zu erhalten, liest es dieselbe Eingabe im Puffer und schlägt erneut fehl. Deshalb brauchst du

cin.ignore(10000,'\n');

Es nimmt 10000 Zeichen aus dem Puffer heraus, stoppt jedoch, wenn es auf eine neue Zeile (\ n) stößt. Der 10000 ist nur ein allgemeiner großer Wert.


10
Wenn Sie nur hinzufügen, dass der Standardwert für das Ignorieren darin besteht, ein einzelnes Zeichen zu überspringen, benötigen Sie eine größere Zahl, um eine ganze Zeile zu überspringen.
Bo Persson

20

Warum verwenden wir:

1) cin.ignore

2) cin.clear

?

Einfach:

1) Ignorieren (Extrahieren und Verwerfen) von Werten, die wir nicht im Stream haben möchten

2) Um den internen Status des Streams zu löschen. Nach Verwendung von cin.clear wird der interne Status wieder auf Goodbit zurückgesetzt, was bedeutet, dass keine "Fehler" vorliegen.

Lange Version:

Wenn etwas auf 'stream' (cin) gestellt wird, muss es von dort genommen werden. Mit "genommen" meinen wir "gebraucht", "entfernt", "extrahiert" aus dem Stream. Stream hat einen Fluss. Die Daten fließen auf cin wie Wasser im Strom. Sie können den Wasserfluss einfach nicht stoppen;)

Schau dir das Beispiel an:

string name; //line 1
cout << "Give me your name and surname:"<<endl;//line 2
cin >> name;//line 3
int age;//line 4
cout << "Give me your age:" <<endl;//line 5
cin >> age;//line 6

Was passiert, wenn der Benutzer antwortet: "Arkadiusz Wlodarczyk" für die erste Frage?

Führen Sie das Programm aus, um sich selbst davon zu überzeugen.

Sie sehen auf der Konsole "Arkadiusz", aber das Programm fragt Sie nicht nach dem Alter. Es wird sofort nach dem Drucken von "Arkadiusz" beendet.

Und "Wlodarczyk" wird nicht gezeigt. Es scheint, als wäre es weg (?) *

Was ist passiert? ;-);

Weil zwischen "Arkadiusz" und "Wlodarczyk" ein Leerzeichen ist.

Das Leerzeichen zwischen Vor- und Nachname ist ein Zeichen für den Computer, dass zwei Variablen darauf warten, im Eingabestream extrahiert zu werden.

Der Computer glaubt, dass Sie mehr als eine Variable an die Eingabe senden möchten. Dieses "Leerzeichen" ist ein Zeichen für ihn, es so zu interpretieren.

Der Computer weist also "Name" (2) "Arkadiusz" zu. Da Sie mehr als eine Zeichenfolge in den Stream (Eingabe) einfügen, versucht der Computer, der Variablen "Alter" (!) Den Wert "Wlodarczyk" zuzuweisen. Der Benutzer hat keine Chance, etwas in Zeile 6 auf das 'cin' zu setzen, da diese Anweisung bereits ausgeführt wurde (!). Warum? Weil noch etwas in Betrieb war. Und wie ich bereits sagte, befindet sich der Stream in einem Fluss, sodass alles so schnell wie möglich entfernt werden muss. Und die Möglichkeit kam, als der Computer den Unterricht sah.

Der Computer weiß nicht, dass Sie eine Variable erstellt haben, in der das Alter einer Person gespeichert ist (Zeile 4). 'Alter' ist nur ein Etikett. Für Computer könnte "Alter" auch als "afsfasgfsagasggas" bezeichnet werden, und es wäre dasselbe. Für ihn ist es nur eine Variable, der er versuchen wird, "Wlodarczyk" zuzuweisen, weil Sie den Computer in Zeile (6) dazu aufgefordert haben.

Es ist falsch, das zu tun, aber hey, du hast es getan! Es ist deine Schuld! Nun, vielleicht Benutzer, aber immer noch ...


Gut gut. Aber wie kann man das beheben?!

Versuchen wir, ein wenig mit diesem Beispiel zu spielen, bevor wir es richtig reparieren, um ein paar weitere interessante Dinge zu lernen :-)

Ich bevorzuge einen Ansatz, bei dem wir die Dinge verstehen. Etwas zu reparieren, ohne zu wissen, wie wir es gemacht haben, gibt keine Befriedigung, meinst du nicht auch? :) :)

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate(); //new line is here :-)

Nach dem Aufrufen des obigen Codes werden Sie feststellen, dass der Status Ihres Streams (cin) gleich 4 ist (Zeile 7). Das heißt, sein interner Zustand ist nicht mehr gleich Goodbit. Etwas ist durcheinander. Es ist ziemlich offensichtlich, nicht wahr? Sie haben versucht, der int-Typvariablen 'age' den Wert für den Zeichenfolgentyp ("Wlodarczyk") zuzuweisen. Typen stimmen nicht überein. Es ist Zeit zu informieren, dass etwas nicht stimmt. Und der Computer ändert dazu den internen Status des Streams. Es ist wie: "Du verdammter Mann, repariere mich bitte. Ich informiere dich 'freundlich' ;-)"

Sie können 'cin' (Stream) einfach nicht mehr verwenden. Es steckt fest. Als ob Sie große Holzscheite auf den Wasserstrahl gelegt hätten. Sie müssen es reparieren, bevor Sie es verwenden können. Daten (Wasser) können nicht mehr von diesem Stream (cin) abgerufen werden, da Holzprotokoll (interner Zustand) dies nicht zulässt.

Oh, wenn es ein Hindernis gibt (Holzstämme), können wir es einfach mit dafür vorgesehenen Werkzeugen entfernen?

Ja!

Der interne Zustand von cin auf 4 ist wie ein Alarm, der heult und Geräusche macht.

cin.clear löscht den Zustand wieder auf Normal (Goodbit). Es ist, als wären Sie gekommen und hätten den Alarm zum Schweigen gebracht. Du hast es einfach aufgeschoben. Sie wissen, dass etwas passiert ist, also sagen Sie: "Es ist in Ordnung, keinen Lärm mehr zu machen. Ich weiß, dass bereits etwas nicht stimmt. Halt die Klappe (klar)."

Also gut, lass es uns tun! Verwenden wir cin.clear ().

Rufen Sie den folgenden Code mit "Arkadiusz Wlodarczyk" als erste Eingabe auf:

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl; 
cin.clear(); //new line is here :-)
cout << cin.rdstate()<< endl;  //new line is here :-)

Wir können sicher nach der Ausführung des obigen Codes sehen, dass der Zustand gleich Goodbit ist.

Großartig, damit das Problem gelöst ist?

Rufen Sie den folgenden Code mit "Arkadiusz Wlodarczyk" als erste Eingabe auf:

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl;; 
cin.clear(); 
cout << cin.rdstate() << endl; 
cin >> age;//new line is here :-)

Auch wenn der Status nach Zeile 9 auf Goodbit gesetzt ist, wird der Benutzer nicht nach "Alter" gefragt. Das Programm stoppt.

WARUM?!

Oh Mann ... Sie haben gerade Alarm geschlagen, was ist mit dem Holzklotz in einem Wasser? * Gehen Sie zurück zu dem Text, in dem wir über "Wlodarczyk" gesprochen haben, wie es angeblich weg war.

Sie müssen "Wlodarczyk" dieses Stück Holz aus dem Strom entfernen. Das Ausschalten von Alarmen löst das Problem überhaupt nicht. Sie haben es gerade zum Schweigen gebracht und denken, das Problem ist weg? ;)

Es ist also Zeit für ein anderes Tool:

cin.ignore kann mit einem speziellen LKW mit Seilen verglichen werden, der kommt und die Holzstämme entfernt, durch die der Strom stecken geblieben ist. Es behebt das Problem, das der Benutzer Ihres Programms erstellt hat.

Könnten wir es also noch verwenden, bevor der Alarm ausgelöst wird?

Ja:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;

Der "Wlodarczyk" wird entfernt, bevor das Geräusch in Zeile 7 gemacht wird.

Was ist 10000 und '\ n'?

Es heißt, 10000 Zeichen entfernen (nur für den Fall), bis '\ n' erfüllt ist (ENTER). Übrigens kann es mit numeric_limits besser gemacht werden, aber es ist nicht das Thema dieser Antwort.


Die Hauptursache des Problems ist also verschwunden, bevor Lärm gemacht wurde ...

Warum brauchen wir dann "klar"?

Was wäre, wenn jemand in Zeile 6 nach der Frage "Gib mir dein Alter" gefragt hätte, zum Beispiel: "20 Jahre alt", anstatt 20 zu schreiben?

Typen stimmen nicht mehr überein. Der Computer versucht, int eine Zeichenfolge zuzuweisen. Und der Alarm beginnt. Sie haben nicht einmal die Chance, auf eine solche Situation zu reagieren. cin.ignore hilft Ihnen in diesem Fall nicht weiter.

Also müssen wir in solchen Fällen klar verwenden:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;
cin.clear();
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

Aber sollten Sie den Staat "nur für den Fall" löschen?

Natürlich nicht.

Wenn etwas schief geht (cin >> age;), werden Sie durch eine falsche Anweisung darüber informiert.

Wir können also eine bedingte Anweisung verwenden, um zu überprüfen, ob der Benutzer einen falschen Typ in den Stream eingegeben hat

int age;
if (cin >> age) //it's gonna return false if types doesn't match
    cout << "You put integer";
else
    cout << "You bad boy! it was supposed to be int";

Also gut, damit wir unser anfängliches Problem beheben können, wie zum Beispiel:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

int age;
cout << "Give me your age:" << endl;
if (cin >> age)
  cout << "Your age is equal to:" << endl;
else
{
 cin.clear();
 cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
 cout << "Give me your age name as string I dare you";
 cin >> age;
}

Dies kann natürlich verbessert werden, indem Sie beispielsweise das, was Sie in Frage gestellt haben, mit loop während ausführen.

BONUS:

Sie fragen sich vielleicht. Was ist, wenn ich vom Benutzer Vor- und Nachnamen in derselben Zeile erhalten möchte? Ist es überhaupt möglich, cin zu verwenden, wenn cin jeden durch "Leerzeichen" getrennten Wert als unterschiedliche Variable interpretiert?

Sicher, Sie können es auf zwei Arten tun:

1)

string name, surname;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin >> surname;

cout << "Hello, " << name << " " << surname << endl;

2) oder mit der Funktion getline.

getline(cin, nameOfStringVariable);

und so geht's:

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Die zweite Option kann nach hinten losgehen, falls Sie sie verwenden, nachdem Sie 'cin' vor der getline verwendet haben.

Schauen wir uns das an:

ein)

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endl;

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Wenn Sie "20" als Alter angeben, werden Sie nicht nach nameAndSurname gefragt.

Aber wenn Sie es so machen:

b)

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endll

alles ist gut.

WAS?!

Jedes Mal, wenn Sie etwas in die Eingabe (Stream) einfügen, verlassen Sie am Ende das weiße Zeichen ENTER ('\ n'). Sie müssen irgendwie Werte für die Konsole eingeben. Es muss also passieren, wenn die Daten vom Benutzer stammen.

b) cin-Merkmale sind, dass Leerzeichen ignoriert werden. Wenn Sie also Informationen aus cin einlesen, spielt das Zeilenumbruchzeichen '\ n' keine Rolle. Es wird ignoriert.

a) Die Funktion getline bringt die gesamte Zeile bis zum Zeilenumbruchzeichen ('\ n'), und wenn das Zeilenumbruchzeichen das erste ist, erhält die Funktion getline '\ n', und das ist alles, was zu bekommen ist. Sie extrahieren ein Zeilenumbruchzeichen, das vom Benutzer im Stream belassen wurde, der in Zeile 3 "20" in den Stream eingefügt hat.

Um dies zu beheben, müssen Sie immer cin.ignore () aufrufen. Jedes Mal, wenn Sie cin verwenden, um einen Wert zu erhalten, wenn Sie getline () jemals in Ihrem Programm verwenden möchten.

Der richtige Code wäre also:

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cin.ignore(); // it ignores just enter without arguments being sent. it's same as cin.ignore(1, '\n') 
cout << "Your age is" << age << endl;


string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Ich hoffe, Streams sind dir klarer.

Hah, bring mich bitte zum Schweigen! :-)


1

Verwenden Sie cin.ignore(1000,'\n')diese Option, um alle Zeichen des vorherigen Zeichens cin.get()im Puffer zu löschen, und es wird angehalten, wenn es '\ n' oder das 1000 charserste Mal erfüllt .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.