Récupérer d'une erreur de lecture sur un flux

On prononce cin comme see-in et cout comme see-out.

Le « c » signifie dans chaque cas character, car à un très bas niveau les entrées/ sorties s'y font byte par byte.

Cliquez ici pour en savoir plus.

Le langage C++, comme la plupart des langages de programmation contemporains, permet de lire et d'écrire de et sur des flux de données, ce qui lui permet d'abstraire les accès aux périphériques comme le clavier, la souris ou un écran console de la même manière que celle utilisée pour abstraire l'accès à un fichier.

Le flux en entrée le plus typique avec C++, surtout en début de formation, est l'objet permettant l'asbtraction de l'entrée standard et nommé std::cin. Cette rubrique utilisera std::cin comme référence mais pourrait s'appliquer à tout dérivé de std::istream (si cette dernière phrase ne vous dit rien, ce n'est sans doute qu'une question de temps).

Un programme simpliste typique illustrant une entrée/ sortie au clavier et à la console serait le suivant :

#include <iostream>
int main()
{
   using namespace std;
   const int BORNE_MIN = 1,
             BORNE_MAX = 10;
   cout << "Entrez un entier se situant entre "
        << BORNE_MIN << " et " << BORNE_MAX
        << " inclusivement. Votre choix : ";
   int choix;
   cin >> choix;
   while (choix < BORNE_MIN || choix > BORNE_MAX)
   {
      cout << "Erreur. Je veux un entier se situant entre "
           << BORNE_MIN << " et " << BORNE_MAX
           << " inclusivement. Votre choix : ";
      cin >> choix;
   }
   cout << "La valeur choisie est: "
        << choix << "; merci!" << endl;
}

Ce programme acceptera un entier au clavier tant et aussi longtemps que l'entier entré ne se situera pas inclusivement entre les bornes exigées (la condition de la répétitive offre cette garantie).

Ce programme n'est par contre pas protégé contre une entrée de quelque chose qui n'est pas un entier. En effet, le flux std::cin représente une séquence de données brutes (celles entrées au clavier), et l'opérateur >> tente de convertir ce qui se trouve au début de cette séquence en quelque chose du type de la variable dans laquelle il tente d'écrire. Si l'usager a entré la lettre 'A', par exemple, la conversion échouera et le programme tombera dans un état instable.

On peut reconnaître l'état instable en question en interrogeant std::cin lui-même. En effet, comme tout flux d'entrée ou de sortie en C++, std::cin est un objet avec lequel il est possible d'interagir. On peut donc solliciter sa méthode fail() pour valider si l'entrée et la conversion qui s'ensuivit furent réalisées avec succès:

#include <iostream>
int main()
{
   using namespace std;
   const int BORNE_MIN = 1,
             BORNE_MAX = 10;
   cout << "Entrez un entier se situant entre "
        << BORNE_MIN << " et " << BORNE_MAX
        << " inclusivement. Votre choix : ";
   int choix;
   cin >> choix;
   if (cin.fail())
      cout << "Vous n'avez pas choisi un entier!"
           << endl;
   else
      cout << "La valeur choisie est: "
           << choix << "; merci!" << endl;
}

Si on désire reprendre la lecture tant et aussi longtemps qu'une valeur à la fois valide et située entre les bornes n'aura pas été saisie, il faudra remettre le flux en entrée sur ses pattes à chaque erreur. En effet, une entrée intraduisible sur un flux d'entrée a deux conséquences :

Une meilleure pratique est de tester le flux suite à la lecture. Un flux se comporte alors comme un booléen, vrai s'il est enb bon état et faux s'il a rencontré une erreur. Ainsi, un meilleur programme serait :

#include <iostream>
int main()
{
   using namespace std;
   const int BORNE_MIN = 1,
             BORNE_MAX = 10;
   cout << "Entrez un entier se situant entre "
        << BORNE_MIN << " et " << BORNE_MAX
        << " inclusivement. Votre choix : ";
   int choix;
   if (!(cin >> choix))
      cout << "Vous n'avez pas choisi un entier!"
           << endl;
   else
      cout << "La valeur choisie est: "
           << choix << "; merci!" << endl;
}

Si on veut lire un entier, comme c'est le cas dans notre exemple, et si le contenu de std::cin est impossible à traduire en entier, alors :

Sachant ceci, voici comment on pourrait réaliser le programme attendu :

#include <iostream>
int main()
{
   using namespace std;
   const int BORNE_MIN = 1,
             BORNE_MAX = 10;
   cout << "Entrez un entier se situant entre "
        << BORNE_MIN << " et " << BORNE_MAX
        << " inclusivement. Votre choix : ";
   int choix;
   while (!(cin >> choix) || choix < BORNE_MIN || choix > BORNE_MAX)
   {
      cin.clear();  // on oublie l'échec et on poursuit
      cin.ignore(); // ignorer le contenu déjà lu
      cout << "Erreur. Je veux un entier se situant entre "
           << BORNE_MIN << " et " << BORNE_MAX
           << " inclusivement. Votre choix : ";
   }
   cout << "La valeur choisie est: "
        << choix << "; merci!" << endl;
}

On pourrait raffiner ce programme de diverses façons (en particulier en passant des paramètres à ignore() pour ignorer le contenu lu jusqu'au premier caractère d'espacement plutôt qu'ignorer tout ce qui a été lu), mais l'idée est là.


Valid XHTML 1.0 Transitional

CSS Valide !