Récupérer proprement des données entrées dans la console

Le problème exposé dans ce sujet a été résolu.

Salut à tout le monde :)

Je voudrais avoir vos retours et conseilles. J’ai une petite application qui fonctionne en console et mon problème se situe au niveau de la récupération d’une donnée entrée dans la console. Pour récupérer un integer j’utilise tout simplement un std: :cin» et pour récupérer un string, j’utilise un std: : getline(). L’application permet de manager des cours et les informations des étudiants. Il y a différentes fonctionnalités et chacune d’entre elles récupère soit un integer entré par l’utilisateur dans la console soit un string ou les 2.

Je voudrais savoir comment récupérer la donnée entrée dans la console de manière propre et peu importe l’ordre.

Quelques exemples de problèmes que j’ai rencontrés

  • Récupérer un integer avant un string : l’instruction pour récupérer le string n’est pas exécutée. Pour pallier à cela, j’ai utilisé le std::cin.ignore() avant le std::getline() pour récupérer le string. Mais cela a conduit au problème suivant.
  • Récupérer 2 strings l’un après l’autre avec un std::cin.ignore() entre les 2. Le code qui suit est un exemple du cas présent et selon la position du std::cin.ignore() et les arguments, le résultat n’est pas satisfaisant.
std::string str{};
std::string str2{};

/*bout de code exécuté par une fonction x*/
std::cout << "entrez str:" << std::endl;
std::getline(std::cin, str);
std::cout << "str = " << str << std::endl;

/*bout de code exécuté par une fonction y appelée directement après l'exécution de la fonction x*/
std::cin.ignore(); // pour pallier le premier problème si la fonction x récupère un integer
//std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "entrer str2:" << std::endl;
//std::cin.ignore();
//std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::getline(std::cin, str2);
std::cout <<"str2 = " << str2 << std::endl;

Merci d’avance pour vos retours :)

Pourquoi pas plutôt un programme qui se lance avec des paramètres ? Un truc qui se lancerait d’une ou plusieurs des façons suivantes :

$ mon_programme -c "ma_chaine" -i 42
> …
$ mon_programme -i 42
> …
$ mon_programme -c "ma_chaine"
> …
$ mon_programme --text_file "/home/donald/dossier à ranger/arguments.txt"
> …
$ mon programme --help
> …

Comme la plupart des programme en Bash en fait. Tu aurais juste à récupérer les paramètres passés par l’utilisateur. C’est un peu plus propre que les cin à la chaîne, qui obligent l’utilisateur à tout recommencer depuis le début à la moindre faute de frappe.

Je suis d’accord que les programmes consoles sont plus généralement manipulés au travers leurs arguments.

Si tu souhaites vraiment faire un programme interactif dans le terminal tel que "top" par exemple, il existe des libs TUI comme ncurses.

Le soucis d’utiliser cin/cout pour faire de l’interactif est que ce ne sont que des flux. Tu vas concevoir ton appli comme un dialogue alors qu’en fait l’utilisateur pourra juste envoyer un fichier dans le flux au lieu de son clavier et ça n’aura plus aucun sens. Et en plus tu n’as aucun contrôle sur l’affichage.

Si, pour diverses raisons, tu veux continuer d’utiliser cin/cout pour faire un programme interactif, j’ai l’impression que ce qu’il te manque c’est juste de finir de consommer la ligne dans chaque cas.

std::cout << "saisir i1 : ";
int i1 = 0;
while (!(std::cin >> i1)) { // On vérifie qu'on lit bien un entier
  if (std::cin.eof()) {
    // fin du flux, interrompre le programme ?
    break; // en tout cas ne pas rester coincé ici
  } else if (std::cin.fail()) {
    std::cin.clear(); // on réinitialise l'erreur puisqu'on la traite
    // on vide le contenu erroné
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    // on prévient l'utilisateur
    std::cout << "Saisie invalide, veuillez saisir à nouveau : ";
  }
}
// on finit de consommer la ligne, car '23 ans' est une entrée valide par
// exemple, et on ne veut pas laisser ' ans' à la lecture suivante
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "i1 = " << i1 << std::endl;

std::cout << "saisir str1 : ";
std::string str1{};
std::getline(std::cin, str1);
std::cout << "str1 = " << str1 << std::endl;

std::cout << "saisir str2 : ";
std::string str2{};
std::getline(std::cin, str2);
std::cout << "str2 = " << str2 << std::endl;

std::cout << "saisir i2 : ";
int i2 = 0;
while (!(std::cin >> i2)) { // On vérifie qu'on lit bien un entier
  if (std::cin.eof()) {
    // fin du flux, interrompre le programme ?
    break; // en tout cas ne pas rester coincé ici
  } else if (std::cin.fail()) {
    std::cin.clear(); // on réinitialise l'erreur puisqu'on la traite
    // on vide le contenu erroné
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    // on prévient l'utilisateur
    std::cout << "Saisie invalide, veuillez saisir à nouveau : ";
  }
}
// on finit de consommer la ligne, car '23 ans' est une entrée valide par
// exemple, et on ne veut pas laisser ' ans' à la lecture suivante
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "i2 = " << i2 << std::endl;

(évidemment tu peux mettre la saisie d’un entier dans une fonction)

+0 -0

Bonjour,

Pour ne pas se compliquer la vie avec les flux, il faut éviter de les mettre en condition d’erreur. Ca évite aussi les cin.clear() et cin.ignore() pas simples à utiliser.

flush( std::cout << "Entrer un entier : " );
std::string  line;
std::getline( std::cin, line );  // extraire toute la ligne
std::istringstream  ss( line );
int  val;
if ( ss >> val ) {
    endl( std::cout << "L'entier : " << val << " a été saisi" );
}
else {
    endl( std::cout << "Erreur, vous n'avez pas saisi un entier" );
}
// le flux std::cin est toujours valide pour extraire une ligne

flush( std::cout << "Entrer une chaîne : " );
std::getline( std::cin, line );  // extraire toute la ligne
endl( std::cout << "La chaîne : " << std::qoted(line) << " a été saisie" );

De la même manière avec std::cout dans une fonction, on peut passer par std::ostringstream. Ainsi une modification de format ne viendra pas "polluer" le format par défaut de std::cout.

+0 -0

L'ignore n’est nécessaire qu’entre un >> et un getline.

Il sera inutile, voire totalement parasite et perturbateur si systématisé sur l’un ou l’autre.

A la limite, il y a >> std::ws comme indiqué dans la FAQ. A vu de nez, on doit pouvoir le systématiser sur l’extraction avec >> — jamais réfléchi plus de 30s à la question.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte