Quelle est la meilleure façon de faire de la validation de l'entrée en C++ avec cin?
Mon frère a récemment commencé à apprendre le C++. Il m'a un problème qu'il a rencontré en essayant de valider l'entrée dans un programme simple. Il avait un menu texte où l'utilisateur a entré un nombre entier choice
, si ils sont entrés dans une défaillance de choix, ils seraient invités à les entrer de nouveau (boucle do while). Toutefois, si l'utilisateur a entré une chaîne au lieu d'un int, le code se briser.
J'ai lu plusieurs questions sur stackoverflow et lui dit de le réécrire son code le long des lignes de:
#include<iostream>
using namespace std;
int main()
{
int a;
do
{
cout<<"\nEnter a number:"
cin>>a;
if(cin.fail())
{
//Clear the fail state.
cin.clear();
//Ignore the rest of the wrong user input, till the end of the line.
cin.ignore(std::numeric_limits<std::streamsize>::max(),\
'\n');
}
}while(true);
return 0;
}
Bien que cela a fonctionné ok, j'ai aussi essayé quelques autres idées:
1. À l'aide d'un bloc try catch. Il n'a pas de travail. Je pense que c'est parce qu'une exception n'est pas soulevée en raison de la mauvaise entrée.
2. J'ai essayé if(! cin){//Do Something}
qui ne fonctionne pas non plus. Je n'ai pas encore compris ça.
3. Troisièmement, j'ai essayé de la saisie d'une chaîne de longueur fixe et ensuite l'analyser. Je voudrais utiliser atoi(). Est-ce conforme aux normes et portable? Dois-je écrire ma propre fonction d'analyse syntaxique?
4. Si écrire une classe qui utilise cin, mais de façon dynamique ce type de détection d'erreur, peut-être par déterminer le type de la variable d'entrée au moment de l'exécution, aurait-il trop de frais généraux? Est-il même possible?
Je voudrais savoir quelle est la meilleure façon de faire ce genre de vérification, quelles sont les meilleures pratiques?
Je tiens à ajouter que, si je ne suis pas nouvelle à l'écriture de code C++, je suis nouveau à la rédaction d'un bon code conforme aux standards. Je suis en train de désapprendre les mauvaises pratiques et d'apprendre le droit. Je serais très reconnaissant si answerers donner une explication détaillée.
MODIFIER: je vois que litb a répondu à une de mes précédentes éditions. Je vais poster le code ici pour référence.
#include<iostream>
using namespace std;
int main()
{
int a;
bool inputCompletionFlag = true;
do
{
cout<<"\nEnter a number:"
cin>>a;
if(cin.fail())
{
//Clear the fail state.
cin.clear();
//Ignore the rest of the wrong user input, till the end of the line.
cin.ignore(std::numeric_limits<std::streamsize>::max(),\
'\n');
}
else
{
inputCompletionFlag = false;
}
}while(!inputCompletionFlag);
return 0;
}
Ce code ne fonctionne pas sur une entrée comme "1asdsdf". Je ne savais pas comment le réparer, mais litb a affiché une grande réponse. 🙂
OriginalL'auteur |
Vous devez vous connecter pour publier un commentaire.
Voici le code que vous pouvez utiliser pour assurez-vous également de rejeter des choses comme
Cas de non-nombre de caractères de suivre le nombre. Si vous lisez l'ensemble de la ligne, puis de l'analyser et de prendre des mesures de manière appropriée, il va peut-être vous obliger à changer la façon dont votre programme fonctionne. Si votre programme de lecture de votre numéro de différents endroits jusqu'à maintenant, vous avez alors de mettre un place centrale qui analyse une ligne de l'entrée, et décide de l'action. Mais peut-être que c'est une bonne chose de trop - de sorte que vous pouvez augmenter la lisibilité du code de cette façon en ayant des choses séparées: jeentree - Processing - Output
De toute façon, ici, c'est comment vous pouvez rejeter le nombre de non-nombre de ci-dessus. Lit une ligne dans une chaîne de caractères, puis de l'analyser avec un
stringstream
:Il mange tous fuite des espaces. Lorsqu'il atteint la fin de fichier de la stringstream lors de la lecture de l'entier de début ou de fin d'espaces, elle définit ensuite la eof-bit, et nous avons vérifier que. Si elle n'a pas pu lire tout entier, en premier lieu, puis de l'échec ou de mauvaise bits.
Les versions antérieures de cette réponse utilisé
std::cin
directement - maisstd::ws
ne fonctionne pas bien avecstd::cin
connecté à un terminal (il permet de bloquer la place d'attente pour l'utilisateur de saisir quelque chose), nous utilisons donc unstringstream
pour la lecture de l'entier.Répondre à certaines de vos questions:
Question: 1. À l'aide d'un bloc try catch. Il n'a pas de travail. Je pense que c'est parce qu'une exception n'est pas soulevée en raison de la mauvaise entrée.
Réponse: Bien, vous pouvez dire le flux de lever des exceptions quand vous lisez quelque chose. Vous utilisez le
istream::exceptions
fonction, que vous dire de quel type d'erreur que vous voulez avoir une exception:Je n'ai jamais l'utiliser. Si vous faites cela sur
std::cin
, vous devrez vous rappeler de restaurer les drapeaux pour les autres lecteurs qui s'appuient sur elle de ne pas jeter. De trouver qu'il est beaucoup plus facile de simplement utiliser les fonctions échouer, mauvais à demander de l'état de flux.Question: 2. J'ai essayé
if(!cin){ //Do Something }
qui ne fonctionne pas non plus. Je n'ai pas encore compris ça.Réponse: Qui pourrait venir du fait que vous avez donné quelque chose comme "42crap". Pour le stream, c'est tout à fait valable d'entrée lors d'une extraction dans un entier.
Question: 3. Troisièmement, j'ai essayé de la saisie d'une chaîne de longueur fixe et ensuite l'analyser. Je voudrais utiliser atoi(). Est-ce conforme aux normes et portable? Dois-je écrire ma propre fonction d'analyse syntaxique?
Réponse: atoi est Conforme à la Norme. Mais il n'est pas bon lorsque vous souhaitez vérifier les erreurs. Il n'y a pas de contrôle d'erreur, fait par elle, par opposition à d'autres fonctions. Si vous avez une chaîne et que vous souhaitez vérifier s'il contient un nombre, alors le faire comme dans le code ci-dessus.
Il y a C-comme des fonctions qui peuvent lire directement à partir d'un C-string. Ils existent pour permettre l'interaction avec le vieux code héritage et l'écriture rapide exécution de code. On devrait éviter dans des programmes parce qu'ils travaillent plutôt faible et nécessitent l'aide de matières nu pointeurs. De par leur nature même, ils ne peuvent pas être amélioré pour fonctionner avec les types définis par l'utilisateur. Plus précisément, cela parle de la fonction "strtol" (chaîne de caractères de long) qui est fondamentalement atoi avec la vérification des erreurs et de la capacité à travailler avec d'autres bases (hex par exemple).
Question: 4. Si j'écris une classe qui utilise cin, mais dynamiquement faire ce genre de détection d'erreur, peut-être par déterminer le type de la variable d'entrée au moment de l'exécution, elle aura trop de frais généraux? Est-il même possible?
Réponse: en Général, vous n'avez pas besoin de trop s'occuper des frais généraux ici (si vous voulez dire à l'exécution, les frais généraux). Mais cela dépend spécifiquement sur l'endroit où vous l'utilisation de cette classe. Cette question sera très important si vous écrivez un système de haute performance que les processus d'entrée et de besoins ont élevé tout au long. Mais si vous avez besoin pour lire l'entrée à partir d'un terminal ou d'un dossier, vous pouvez déjà voir ce que cela se résume à: temps d'Attente pour l'utilisateur de saisir quelque chose prend vraiment si longtemps, vous n'avez pas besoin de regarder runtime dépens, à ce point de plus sur cette échelle.
Si vous dire code frais - bien cela dépend de la façon dont le code est mis en œuvre. Vous devez analyser votre chaîne que vous avez lu - s'il contient un nombre ou pas, si une certaine chaîne de caractères arbitraire. Selon ce que vous souhaitez numériser (peut-être que vous avez une "date d'entrée", ou un "temps" format d'entrée trop. Regarder dans
boost.date_time
pour ça), votre code peut devenir arbitrairement complexe. Pour des choses simples comme la classification entre le nombre ou pas, je pense que vous pouvez vous en sortir avec une petite quantité de code.Je viens de remarquer qu'il va être normalisée dans une large mesure dans C++0x. Je vais donc utiliser Boost. Je serai heureux si vous le confirmer. Merci.
ouais, la prochaine c++ comprendra certaines bibliothèques été conçu d'après le boost (shared_ptr, le fil, le système (code_erreur, ...), array bind, fonction). mais pas date_time. boost est cependant fortement recommandé. c'est mieux que de rouler votre propre vraiment
Merci pour la réponse litb. Je vais aller avec Boost
OriginalL'auteur Johannes Schaub - litb
C'est ce que je fais avec C, mais c'est probablement applicable pour le C++.
D'entrée tout comme une chaîne de caractères.
Alors, et alors seulement, analyser la chaîne dans ce que vous avez besoin. Il est parfois préférable de faire votre propre code que d'essayer de plier quelqu'un d'autre à ta volonté.
OriginalL'auteur paxdiablo
OriginalL'auteur Anonymous
Ce que je voudrais faire est double: d'Abord, essayez de valider l'entrée, et d'extraire les données, à l'aide d'une expression régulière, si l'entrée est un peu pas trivial. Il peut être très utile, même si l'entrée est juste une série de nombres.
Puis, j'aime utiliser boost::lexical_ cast, ce qui peut soulever un bad_ lexical_ cast exception si l'entrée ne peut pas être converti.
Dans votre exemple:
OriginalL'auteur Diego Sevilla
Oublier d'utiliser formaté à l'entrée (l' >> opérateur) directement dans le code réel. Vous aurez toujours besoin de lire de texte brut avec std::getline ou similaire, et ensuite utiliser votre propre entrée des routines d'analyse (qui peut utiliser de la >> opérateur) pour analyser l'entrée.
OriginalL'auteur
Comment agit d'une combinaison de différentes approches:
De s'accrocher à l'entrée de
std::cin
à l'aide destd::getline(std::cin, strObj)
oùstrObj
est unstd::string
objet.Utilisation
boost::lexical_cast
pour effectuer une traduction lexicale à partir destrObj
soit un entier signé ou non signé de plus grande largeur (par exemple,unsigned long long
ou quelque chose de similaire)Utilisation
boost::numeric_cast
de jeter l'entier vers le bas de la fourchette prévue.Vous pouvez simplement aller chercher de l'entrée avec
std::getline
et ensuite appelerboost::lexical_cast
pour le bien étroit de type entier, en fonction de l'endroit où vous voulez attraper l'erreur. Les trois étapes de l'approche a l'avantage d'accepter tout entier de données et ensuite de prendre le rétrécissement des erreurs séparément.OriginalL'auteur D.Shawley
Je suis d'accord avec Pax, le moyen le plus simple pour ce faire est de lire tout comme une chaîne de caractères, puis utilisez TryParse pour vérifier l'entrée. Si c'est dans le bon format, puis de procéder, sinon c'est juste en informer l'utilisateur et de l'utilisation de poursuivre sur la boucle.
Je me demandais à ce sujet harper. merci pour la clarification. Merci de répondre à Rekreativc
OriginalL'auteur David Božjak
Une chose qui n'a pas encore été mentionnés, c'est qu'il est généralement important que vous testez pour voir si le cin >> opération travaillé avant d'utiliser la variable qui est censé être vous avez quelque chose à partir du flux.
Cet exemple est similaire à la vôtre, mais ne fait que tester.
Il utilise l'habitude de l'opérateur >> sémantique; il va ignorer les espaces blancs, puis essayez de lire autant de chiffres qu'il peut et puis s'arrêter. Donc "42crap" vous donnera l'42 ensuite, sautez sur la "merde". Si ce n'est pas ce que vous voulez, alors je suis d'accord avec les réponses précédentes, vous devriez le lire en une chaîne de caractères, puis à valider (peut-être à l'aide d'une expression régulière - mais que peut-être exagéré pour une simple séquence numérique).
OriginalL'auteur