web-dev-qa-db-fra.com

Pourquoi devrions-nous appeler cin.clear () et cin.ignore () après avoir lu l'entrée?

tutoriel C++ de Google Code University utilisait ce 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;
}

Quelle est la signification de cin.clear() et cin.ignore()? Pourquoi les paramètres 10000 Et \n Sont-ils nécessaires?

71
JacKeown

La cin.clear() efface l'indicateur d'erreur sur cin (pour que les futures opérations d'E/S fonctionnent correctement), puis cin.ignore(10000, '\n') passe à la nouvelle ligne (pour tout ignorer). sinon sur la même ligne que le non-numéro afin qu’il ne provoque pas un autre échec d’analyse). Il ne sautera que 10 000 caractères au maximum; le code suppose donc que l'utilisateur ne mettra pas une très longue ligne non valide.

87
Jeremiah Willcock

Vous entrez le

if (!(cin >> input_var))

déclaration si une erreur survient lors de la saisie de cin. Si une erreur se produit, un indicateur d'erreur est défini et les tentatives suivantes pour obtenir une entrée échouent. C'est pourquoi tu as besoin

cin.clear();

se débarrasser du drapeau d'erreur. En outre, l'entrée qui a échoué restera dans ce que je suppose être une sorte de tampon. Lorsque vous essayez à nouveau d'entrer, il lira la même entrée dans la mémoire tampon et échouera à nouveau. C'est pourquoi tu as besoin

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

Il extrait 10000 caractères du tampon mais s’arrête s’il rencontre une nouvelle ligne (\ n). Le 10000 est juste une grande valeur générique.

32
flight

Pourquoi utilisons-nous:

1) cin.ignore

2) cin.clear

?

Simplement:

1) Ignorer (extraire et ignorer) les valeurs que nous ne voulons pas dans le flux

2) Effacer l'état interne du flux. Après utilisation de cin.clear, l'état interne est remis sur goodbit, ce qui signifie qu'il n'y a pas d'erreurs.

Version longue:

Si quelque chose est mis sur 'stream' (cin), il faut le prendre à partir de là. Par "pris", nous entendons "utilisé", "retiré", "extrait" du flux. Stream a un flux. Les données circulent comme de l'eau en marche. Vous ne pouvez tout simplement pas arrêter l'écoulement de l'eau;)

Regardez l'exemple:

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

Que se passe-t-il si l'utilisateur répond: "Arkadiusz Wlodarczyk" à la première question?

Exécutez le programme pour voir par vous-même.

Vous verrez sur la console "Arkadiusz" mais le programme ne vous demandera pas "d'âge". Il va juste finir juste après l'impression "Arkadiusz".

Et "Wlodarczyk" n'est pas montré. On dirait que si c'était parti (?) *

Qu'est-il arrivé? ;-)

Parce qu'il y a un espace entre "Arkadiusz" et "Wlodarczyk".

Le caractère "espace" entre le nom et le nom de famille indique à l'ordinateur que deux variables attendent d'être extraites dans le flux "d'entrée".

L'ordinateur pense que vous vous attachez pour envoyer plusieurs entrées à l'entrée. Ce signe "d'espace" est un signe pour lui de l'interpréter de cette façon.

Donc, l'ordinateur assigne "Arkadiusz" à "nom" (2) et, parce que vous mettez plus d'une chaîne dans le flux (entrée), l'ordinateur essaiera d'assigner la valeur "Wlodarczyk" à la variable "age" (!). L'utilisateur n'aura aucune chance de mettre quoi que ce soit sur le "cin" dans la ligne 6 car cette instruction a déjà été exécutée (!). Pourquoi? Parce qu'il restait encore quelque chose en cours. Et comme je l’ai dit précédemment, stream est dans un flux donc tout doit en être retiré dès que possible. Et la possibilité est venue quand l'ordinateur a vu l'instruction cin >> age;

L'ordinateur ne sait pas que vous avez créé une variable qui enregistre l'âge de quelqu'un (ligne 4). 'age' est simplement une étiquette. Pour l'ordinateur, "age" pourrait aussi bien s'appeler: "afsfasgfsagasggas" et ce serait la même chose. Pour lui, il s’agit d’une variable à laquelle il essaiera d’assigner "Wlodarczyk" car vous avez demandé à l’ordinateur de le faire à la ligne (6).

C'est faux, mais c'est toi qui l'as fait! C'est ta faute! Eh bien, peut-être utilisateur, mais quand même ...


D'accord, d'accord. Mais comment y remédier?!

Essayons de jouer avec cet exemple un peu avant de le corriger correctement pour apprendre quelques choses plus intéressantes :-)

Je préfère faire une approche où nous comprenons les choses. Réparer quelque chose sans savoir comment on l'a fait ne donne pas satisfaction, vous ne pensez pas? :)

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 :-)

Après avoir appelé le code ci-dessus, vous remarquerez que l’état de votre flux (cin) est égal à 4 (ligne 7). Ce qui signifie que son état interne n'est plus égal à goodbit. Quelque chose est foiré. C'est assez évident, n'est-ce pas? Vous avez tenté d'affecter une valeur de type chaîne ("Wlodarczyk") à la variable de type int 'age'. Les types ne correspondent pas. Il est temps d'informer que quelque chose ne va pas. Et l'ordinateur le fait en changeant l'état interne du flux. C'est comme: "Tu es foutu mec, répare-moi s'il te plaît. Je t'informe 'gentiment' ;-)"

Vous ne pouvez tout simplement plus utiliser 'cin' (flux). C'est coincé. Comme si vous aviez mis de grosses bûches de bois sur un jet d'eau. Vous devez le réparer avant de pouvoir l'utiliser. Les données (eau) ne peuvent plus être obtenues à partir de ce flux (cin) car le journal du bois (état interne) ne vous permet pas de le faire.

Oh, donc s'il y a un obstacle (bûches de bois), on peut simplement l'enlever en utilisant des outils conçus pour le faire?

Oui!

l’état interne de cin réglé sur 4 est comme une alarme qui hurle et fait du bruit.

cin.clear efface l'état normal (goodbit). C'est comme si vous étiez venus faire taire l'alarme. Vous venez de le remettre. Vous savez que quelque chose est arrivé, alors vous dites: "C'est bon d'arrêter de faire du bruit, je sais que quelque chose ne va pas déjà, tais-toi (clair)".

Très bien faisons-le! Utilisons cin.clear ().

Appelez le code ci-dessous en utilisant "Arkadiusz Wlodarczyk" comme première entrée:

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 :-)

Après l'exécution du code ci-dessus, nous pouvons sûrement voir que l'état est égal à goodbit.

Génial, le problème est-il résolu?

Appelez le code ci-dessous en utilisant "Arkadiusz Wlodarczyk" comme première entrée:

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 :-)

Même si l'état est défini sur goodbit après la ligne 9, l'utilisateur n'est pas invité à indiquer son "âge". Le programme s'arrête.

POURQUOI?!

Oh mec ... Tu as juste mis l'alarme, qu'en est-il de la bûche de bois dans une eau? * Retourne au texte où nous avons parlé de "Wlodarczyk" et de la manière dont elle était supposée avoir disparu.

Vous devez enlever "Wlodarczyk" ce morceau de bois du ruisseau. Désactiver les alarmes ne résout pas le problème du tout. Vous venez de le réduire au silence et vous pensez que le problème a disparu? ;)

Il est donc temps d’utiliser un autre outil:

cin.ignore peut être comparé à un camion spécial muni de cordes qui enlève les bûches de bois bloquées par le courant. Cela efface le problème créé par l'utilisateur de votre programme.

Donc, pourrions-nous l'utiliser avant même que l'alarme ne se déclenche?

Oui:

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;

Le "Wlodarczyk" va être supprimé avant de faire le bruit en ligne 7.

Qu'est-ce que 10000 et '\ n'?

Il dit supprimer 10000 caractères (au cas où) jusqu'à ce que '\ n' soit rencontré (ENTRER). BTW On peut faire mieux en utilisant numeric_limits mais ce n’est pas le sujet de cette réponse.


Donc, la principale cause du problème a disparu avant que le bruit ne soit fait ...

Pourquoi avons-nous besoin de "clair" alors?

Et si quelqu'un avait demandé à la ligne 6 de répondre à la question "Donnez-moi votre âge", par exemple: "vingt ans" au lieu d'écrire 20?

Les types ne correspondent plus. L'ordinateur essaie d'attribuer une chaîne à int. Et l'alarme commence. Vous n'avez même pas la possibilité de réagir à une telle situation. cin.ignore ne vous aidera pas dans ce cas.

Nous devons donc utiliser clair dans ce cas:

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

Mais devriez-vous effacer l'état "juste au cas où"?

Bien sûr que non.

Si quelque chose ne va pas (cin >> age;) l'instruction vous en informera en renvoyant false.

Nous pouvons donc utiliser une instruction conditionnelle pour vérifier si l'utilisateur a mis un type incorrect sur le flux

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

Très bien pour que nous puissions résoudre notre problème initial, comme par exemple:

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

Bien sûr, ceci peut être amélioré en effectuant par exemple ce que vous avez fait en utilisant une boucle.

PRIME:

Vous pourriez vous demander. Et si je voulais avoir le nom et le prénom de l'utilisateur sur la même ligne? Est-il même possible d'utiliser cin si cin interprète chaque valeur séparée par "espace" en tant que variable différente?

Bien sûr, vous pouvez le faire de deux manières:

1)

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

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

2) ou en utilisant la fonction getline.

getline(cin, nameOfStringVariable);

et c'est comment le faire:

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

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

La deuxième option pourrait vous renverser au cas où vous l'utiliseriez après avoir utilisé 'cin' avant la getline.

Regardons ça:

une)

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;

Si vous mettez "20" en tant qu'âge, on ne vous demandera pas nameAndSurname.

Mais si vous le faites de cette façon:

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

tout va bien.

QUOI?!

Chaque fois que vous mettez quelque chose en entrée (flux), vous laissez à la fin le caractère blanc qui est ENTER ('\ n'). Vous devez en quelque sorte entrer des valeurs pour la console. Donc, cela doit arriver si les données proviennent de l'utilisateur.

b) Les caractéristiques de cin sont qu'il ignore les espaces. Ainsi, lorsque vous lisez des informations dans cin, le caractère de nouvelle ligne '\ n' n'a pas d'importance. Il est ignoré.

a) La fonction getline obtient la ligne entière jusqu'au caractère de nouvelle ligne ('\ n'), et lorsque le caractère de nouvelle ligne est la première chose que la fonction getline obtient '\ n', et c'est tout ce que vous voulez obtenir. Vous extrayez le caractère de nouvelle ligne laissé sur le flux par l'utilisateur qui a mis "20" sur le flux de la ligne 3.

Donc, pour y remédier, il faut toujours invoquer cin.ignore (); chaque fois que vous utilisez cin pour obtenir une valeur quelconque si vous utilisez un jour getline () dans votre programme.

Donc, le code approprié serait:

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;

J'espère que les flux sont plus clairs pour vous le savez.

Ah silence me s'il vous plaît! :-)

5
Morfidon

utilisez cin.ignore(1000,'\n') pour effacer tous les caractères de la précédente cin.get() dans le tampon et il choisira de s’arrêter quand il se rencontrera '\ n' ou 1000 chars première.

2
Phy Lieng