J'ai écrit un programme très basique en C++ qui demandait à l'utilisateur de saisir un nombre, puis une chaîne. À ma grande surprise, lors de l'exécution du programme, il n'a jamais cessé de demander la chaîne. Il a juste sauté dessus. Après avoir lu quelque chose sur StackOverflow, j'ai découvert que je devais ajouter une ligne disant:
cin.ignore(256, '\n');
avant la ligne qui obtient la chaîne d'entrée. Ajouter cela a résolu le problème et fait fonctionner le programme. Ma question est la suivante: pourquoi le C++ a-t-il besoin de cette ligne cin.ignore()
et comment puis-je prédire quand il me faudra utiliser cin.ignore()
?
Voici le programme que j'ai écrit:
#include <iostream>
#include <string>
using namespace std;
int main()
{
double num;
string mystr;
cout << "Please enter a number: " << "\n";
cin >> num;
cout << "Your number is: " << num << "\n";
cin.ignore(256, '\n'); // Why do I need this line?
cout << "Please enter your name: \n";
getline (cin, mystr);
cout << "So your name is " << mystr << "?\n";
cout << "Have a Nice day. \n";
}
Ignorer est exactement ce que son nom implique.
Il ne "jette" pas quelque chose dont vous n'avez pas besoin à la place, il ignore le nombre de caractères que vous spécifiez lorsque vous l'appelez, jusqu'au caractère que vous spécifiez en tant que point d'arrêt.
Cela fonctionne avec les tampons d'entrée et de sortie.
Essentiellement, pour les instructions std::cin
que vous utilisez, ignorez avant de faire un appel getline
, car lorsqu'un utilisateur saisit quelque chose avec std::cin
, il appuie sur Entrée et un caractère '\n'
entre dans le cin
tampon. Ensuite, si vous utilisez getline
, il obtient le caractère de nouvelle ligne au lieu de la chaîne souhaitée. Donc vous faites une std::cin.ignore(1000,'\n')
et cela devrait effacer le tampon jusqu'à la chaîne que vous voulez. (Le caractère 1000 est placé là pour ignorer une quantité spécifique de caractères avant le point de rupture spécifié, dans ce cas, le caractère de nouvelle ligne\n.)
Vous pensez à ce sujet dans le mauvais sens. Vous pensez par étapes logiques chaque fois que vous utilisez cin
ou getline
. Ex. Commencez par demander un numéro, puis demandez un nom. C’est la mauvaise façon de penser à cin
. Vous rencontrez donc une situation de concurrence critique parce que vous supposez que le flux est clair chaque fois que vous demandez une entrée.
Si vous écrivez votre programme uniquement à des fins de saisie, vous trouverez le problème:
void main(void)
{
double num;
string mystr;
cin >> num;
getline(cin, mystr);
cout << "num=" << num << ",mystr=\'" << mystr << "\'" << endl;
}
Dans ce qui précède, vous pensez "commencez par obtenir un numéro". Donc, vous tapez 123
appuyez sur enter, et votre sortie sera num=123,mystr=''
. Pourquoi donc? C'est parce que dans le flux vous avez 123\n
et que le 123
est analysé dans la variable num
alors que \n
est toujours dans le flux. En lisant le document pour la fonction getline
par défaut, il regardera dans le istream
jusqu'à ce qu'un \n
soit rencontré. Dans cet exemple, puisque \n
se trouve dans le flux, il semble que cela l'ait "ignoré" mais cela a fonctionné correctement.
Pour que ce qui précède fonctionne, vous devez entrer 123Hello World
qui générera correctement num=123,mystr='Hello World'
. Cela, ou vous mettez un cin.ignore
entre le cin
et le getline
pour qu'il entre dans les étapes logiques que vous attendez.
C'est pourquoi vous avez besoin de la commande ignore
. Parce que vous y réfléchissez par étapes logiques plutôt que sous forme de flux, vous êtes donc dans une situation de concurrence critique.
Prenons un autre exemple de code courant dans les écoles:
void main(void)
{
int age;
string firstName;
string lastName;
cout << "First name: ";
cin >> firstName;
cout << "Last name: ";
cin >> lastName;
cout << "Age: ";
cin >> age;
cout << "Hello " << firstName << " " << lastName << "! You are " << age << " years old!" << endl;
}
Ce qui précède semble être en étapes logiques. Demandez d’abord votre nom, votre prénom, puis votre âge. Donc, si vous avez entré John
, puis Doe
entrez, puis 19
entrez, l'application exécute chaque étape logique. Si vous y réfléchissez dans les "flux", vous pouvez simplement entrer John Doe 19
dans la question "Prénom:". Cela fonctionnera également et semblera ignorer les questions restantes. Pour que ce qui précède fonctionne par étapes logiques, vous devez utiliser ignore
le flux restant pour chaque rupture logique dans les questions.
N'oubliez pas de penser à l'entrée de votre programme, car il lit un "flux" et non pas par étapes logiques. Chaque fois que vous appelez cin
, il est lu à partir d'un flux. Cela crée une application plutôt boguée si l'utilisateur entre la mauvaise entrée. Par exemple, si vous entrez un caractère où un cin >> double
est attendu, l'application produira une sortie plutôt (apparemment) bizarre.
Lorsque vous souhaitez supprimer manuellement un nombre spécifique de caractères du flux d'entrée.
Un cas d'utilisation très courant consiste à utiliser ceci pour ignorer en toute sécurité les caractères de nouvelle ligne puisque cin laisse parfois des caractères de nouvelle ligne que vous devrez parcourir pour passer à la ligne suivante.
En résumé, cela vous donne de la flexibilité pour gérer les entrées de flux.
Réponse courte
Pourquoi? Parce qu'il reste encore des espaces (retours à la ligne, tabulations, espaces, nouvelle ligne) dans le flux d'entrée.
Quand? Lorsque vous utilisez une fonction qui n’ignore pas à elle seule les blancs. Par défaut, Cin ignore et supprime les espaces, mais getline n'ignore pas lui-même les espaces.
Maintenant une réponse détaillée.
Tout ce que vous entrez dans la console est lu à partir du flux standard stdin. Lorsque vous entrez quelque chose, disons 256 dans votre cas et appuyez sur Entrée, le contenu du flux devient 256\n
. Maintenant, cin en récupère 256 et le supprime du flux et \n
reste dans le flux. Maintenant, lorsque vous entrez votre nom, disons Raddicus
, le nouveau contenu du flux est \nRaddicus
.
Maintenant, voici la prise. Lorsque vous essayez de lire une ligne à l'aide de getline, si aucun délimiteur n'est fourni comme troisième argument, getline par défaut lit jusqu'au caractère de nouvelle ligne et supprime le caractère de nouvelle ligne du flux. Donc, en appelant new line, getline lit et supprime \n
du flux et aboutit à une chaîne vide lue dans mystr qui apparaît comme si getline est ignoré (mais ce n'est pas le cas) car il y avait déjà une nouvelle ligne dans le flux, getline will not Invite à la saisie car il a déjà lu ce qu'il était censé lire.
Comment cin.ignore vous aide-t-il ici?
Selon l’extrait de documentation ignorer de cplusplus.com -
istream & ignore (streamize n = 1, int delim = EOF);
Extrait les caractères de la séquence d'entrée et les supprime, jusqu'à ce que n caractères aient été extraits ou que l'on compare égal à délimiter.
La fonction arrête également d'extraire des caractères si la fin du fichier est atteinte. Si ceci est atteint prématurément (avant d'extraire n caractères ou de trouver delim), la fonction active le drapeau eofbit.
Donc, cin.ignore(256, '\n');
, ignore les 256 premiers caractères ou tout le caractère jusqu’à ce qu’il rencontre un délimiteur (ici, dans votre cas), quelle que soit la première occurrence (ici, le premier caractère, ainsi, jusqu’à ce que\n soit rencontré). .
Juste pour votre information, si vous ne connaissez pas exactement le nombre de caractères à ignorer et que votre seul objectif est d'effacer le flux afin de préparer la lecture d'une chaîne à l'aide de getline ou cin, vous devez utiliser cin.ignore(numeric_limits<streamsize>::max(),'\n')
.
Explication rapide: Il ignore les caractères égaux à la taille maximale du flux ou jusqu'à l'apparition d'un '\ n', quel que soit le cas qui se présente en premier.
La fonction Ignorer est utilisée pour ignorer (rejeter/jeter) des caractères dans le flux d'entrée. Ignorer le fichier est associé au fichier istream. Considérons la fonction ci-dessous ex: cin.ignore (120, '/ n'); la fonction particulière ignore les 120 caractères suivants ou ignore les caractères jusqu'à la lecture d'un caractère de nouvelle ligne.