2010-01-09 10 views
3

Lorsque vous exécutez le code suivant et entrez un nombre, cela fonctionne correctement. Mais lors de la saisie d'une lettre, le programme entre dans une boucle infinie, affichant "Enter a number (0 to exit): cin failed".C++ cin.fail() question

Mon intention était de gérer le cas d'échec de cin et inviter l'utilisateur à nouveau.

int number; 
do{ 
    cout << "Enter a number (0 to exit): "; 
    cin >> number; 
    if(cin.fail()){ 
     cout << "cin failed." << endl; 
     cin.clear(); 
    }else{ 
     cout << "cin succeeded, " << number << " entered." << endl; 
    } 
}while(number != 0); 

Répondre

5

Vous devez effacer la ligne de cin, en utilisant cin.ignore, en plus de dégager l'état des cours d'eau (ce qui est ce que cin.clear fait).

J'ai several utility functions pour vous faciliter la tâche (vous serez intéressé par Clearline en particulier, ce qui efface l'état des cours d'eau et la ligne en cours) et presque un exemple exact de what you want.

Votre code, plus ou moins, en utilisant mon Clearline:

#include "clinput.hpp" // move my file to a location it can be used from 

int main() { 
    using namespace std; 
    while (true) { 
    cout << "Enter a number (0 to exit): "; 
    int number; 
    if (cin >> number) { 
     cout << "Read " << number << '\n'; 
     if (number == 0) { 
     break; 
     } 
    } 
    else { 
     if (cin.eof()) { // tested only *after* failed state 
     cerr << "Input failed due to EOF, exiting.\n"; 
     return 1; 
     } 
     cerr << "Input failed, try again.\n"; 
     clearline(cin); // "cin >> clearline" is identical 
    } 
    } 
    return 0; 
} 

Il reste un problème potentiel ici (fixe dans mon clinput_loop.cpp avec blankline), avec laissant entrée dans le tampon qui sera bousiller plus tard IO (voir "42 abc" dans la session d'exemple).Extraire le code ci-dessus dans une fonction distincte et autonome est laissé comme un exercice pour le lecteur, mais voici un squelette:

template<class Type, class Ch, class ChTr> 
Type read(std::basic_istream<Ch,ChTr>& stream, Ch const* prompt) { 
    Type value; 
    // *try input here* 
    if (could_not_get_input or more_of_line_left) { 
    throw std::runtime_error("..."); 
    } 
    return value; 
} 
template<class Type, class Ch, class ChTr> 
void read_into(
    Type& value, 
    std::basic_istream<Ch,ChTr>& stream, 
    Ch const* prompt 
) { 
    value = read<Type>(stream, prompt); 
} 

Exemple d'utilisation:

int n; 
try { 
    read_into(n, std::cin, "Enter a number: "); 
} 
catch (std::runtime_error& e) { 
    //... 
    raise; 
} 
cout << "Read " << n << '\n'; 

Clearline fonction extrait pour la postérité, au cas où les liens ci-dessus se cassent (et légèrement modifiés pour rendre autonomes):

#include <istream> 
#include <limits> 

template<class C, class T> 
std::basic_istream<C,T>& clearline(std::basic_istream<C,T>& s) { 
    s.clear(); 
    s.ignore(std::numeric_limits<std::streamsize>::max(), s.widen('\n')) 
    return s; 
} 

Le truc de modèle est un peu confus si vous n'êtes pas habitué, mais ce n'est pas difficile:

  • std :: istream est un typedef de std::basic_istream<char, std::char_traits<char> >
  • std :: wistream est un typedef de std::basic_istream<wchar_t, std::char_traits<wchar_t> >
  • élargir permet '\n' de devenir L'\n' selon le cas
  • ce code fonctionne pour les deux de la commune omble et wchar_t cas, mais aussi une instanciation compatible basic_istream
  • il est écrit à être appelé comme clearline(stream)oustream >> clearline, comparer à d'autres manipulateurs comme std :: endl, std :: ws ou std :: boolalpha
+0

Merci! Cela l'a réparé. Un point supplémentaire est que j'ai dû utiliser les deux, cin.clear() et cin.ignore(). –

+0

Oui, si vous regardez ma fonction clearline (dans clinput.hpp, et suivez le chemin du code), elle appelle à la fois clear et ignore. –

+0

+1 pour une raison évidente –

1

C'est probablement ce que vous aviez l'intention de le faire:

#include <iostream> 
using namespace std; 

int main() 
{ 
    int i; 
    do { 
    if (cin.fail()) 
    { 
     cin.ignore(255); 
     cin.clear(); 
    }   
    cout << "Please enter an integer value: "; 
    cin >> i; 
    } while (cin.fail()); 
    cout << "The value you entered is " << i; 
    return 0; 
} 
+1

Cela entraînera toujours une boucle infinie, car vous ne modifiez pas l'état du flux ni le tampon du flux une fois qu'il a atteint un état d'échec. –

+0

"Toujours résultat .." si une mauvaise entrée est entrée la première fois, c'est-à-dire. –

+0

Heh, on dirait qu'on apprend quelque chose de nouveau chaque jour ... maintenant c'est correct? (pas de compilateur en main pour tester ...) –

0

Ceci est simple exemple de cin.fail() On traitera entrée jusqu'à une valeur entière valide est fournie

#include <iostream> 
using namespace std; 

int main() 
{ 
int j; 
int i; 

i = 0; 
while (1) { 
    i++; 
    cin >> j; 
    if (cin.fail()) return 0; 
    cout << "Integer " << i << ": " << j << endl; 
} 
} 

entrée:

42 51 85 85 bonjour

Sortie:

Entier 1: 42

Entier 2: 51

Entier 3: 85

+0

Veuillez faire un [edit] pour expliquer ce que fait votre code. –