2012-03-19 16 views
5

Supponiamo di avere un menu che presenta all'utente alcune opzioni:Strano comportamento durante la lettura in int da STDIN

Welcome: 
1) Do something 
2) Do something else 
3) Do something cool 
4) Quit 

L'utente può premere 1 - 4 e poi il tasto Invio. Il programma esegue questa operazione e quindi presenta il menu all'utente. Un'opzione non valida dovrebbe solo visualizzare nuovamente il menu.

Ho la main() seguente metodo:

int main() 
{ 
    while (true) 
     switch (menu()) 
     { 
      case 1: 
       doSomething(); 
       break; 
      case 2: 
       doSomethingElse(); 
       break; 
      case 3: 
       doSomethingCool(); 
       break; 
      case 4: 
       return 0; 
      default: 
       continue; 
     } 
} 

e il follwing menu():

int menu() 
{ 
    cout << "Welcome:" << endl 
     << "1: Do something" << endl 
     << "2: Do something else" << endl 
     << "3: Do something cool" << endl 
     << "4: Quit" << endl; 

    int result = 0; 
    scanf("%d", &result); 
    return result; 
} 

Entrando tipi numerici grandi opere. L'immissione di 1 - 4 fa sì che il programma esegua l'azione desiderata e in seguito il menu viene nuovamente visualizzato. Immettendo un numero al di fuori di questo intervallo, ad esempio -1 o 12, verrà visualizzato di nuovo il menu come previsto.

Tuttavia, l'inserimento di qualcosa come "q" causerà semplicemente la visualizzazione infinita del menu, senza nemmeno fermarsi per ottenere l'input dell'utente.

Non capisco come possa accadere. Chiaramente, menu() viene chiamato come il menu viene visualizzato più e più volte, tuttavia scanf() è parte di menu(), quindi non capisco come il programma ottiene in questo stato di errore in cui l'utente non viene richiesto per il loro input.

Originariamente avevo cin >> result che ha fatto esattamente la stessa cosa.

Edit: Ci sembra essere un related question, tuttavia l'originale source code è scomparso da pastebin e uno dei link risposte a un article che a quanto pare, una volta spiegato perché questo sta accadendo, ma ora è un link morto. Forse qualcuno può rispondere con il motivo per cui questo sta accadendo piuttosto che il collegamento? :)

Edit: Utilizzando this example, ecco come ho risolto il problema:

int getNumericalInput() 
{ 
    string input = ""; 
    int result; 

    while (true) 
    { 
     getline(cin, input); 

     stringstream sStr(input); 
     if (sStr >> result) 
      return result; 

     cout << "Invalid Input. Try again: "; 
    } 
} 

e ho semplicemente sostituito

int result = 0; 
scanf("%d", &result); 

con

int result = getNumericalInput(); 
+0

possibile duplicato di [Una semplice domanda su cin] (http://stackoverflow.com/questions/5864540/a-simple-question-about-cin) –

risposta

6

Quando si tenta per convertire l'input non numerico in un numero, fallisce e (la parte importante) lascia quei dati nel buffer di input, quindi la prossima volta che provate a leggere un int, è ancora lì in attesa, e fallisce ancora - e ancora e ancora, per sempre.

Ci sono due modi per evitare questo. Quello che preferisco è leggere una stringa di dati, quindi convertirla da un numero e prendere l'azione appropriata. In genere utilizzerai std::getline per leggere tutti i dati fino alla nuova riga, quindi prova a convertirli. Dal momento che leggerà qualunque dato ti stia aspettando, non sarai mai bloccato nella posta indesiderata.

L'alternativa è (soprattutto se una conversione non riesce) per utilizzare std::ignore per leggere i dati dall'input fino a (in genere) alla nuova riga successiva.

4

1) Dire questo a te stesso 1000 volte, o fino a quando si addormenta:


          sarò mai mai mai utilizzare le funzioni di I/O senza controllare il valore di ritorno.


2) Ripetere i 50 volte.

3) Rileggere il codice: stai verificando il risultato di scanf? Cosa succede quando scanf non è in grado di convertire l'input nel formato desiderato? Come faresti a imparare queste informazioni se non lo sapessi? (Quattro lettere vengono in mente.)

Vorrei anche mettere in discussione il motivo per cui dovresti usare scanf piuttosto che l'operazione iostreams più appropriata, ma che risentirebbe esattamente dello stesso problema.

+1

Accetto la risposta di Jerry perché in realtà ha spiegato che un fallimento la conversione lascia l'input dell'utente nel buffer di input, causandone il ripetersi all'infinito. Questo non solo mi aiuta, ma chiunque altro si imbatte in questa domanda. Inoltre lo ha spiegato senza essere accondiscendente :) – Ozzah

+0

@Ozzah: certo, nessun problema. Basta fare attenzione con I/O in futuro. –

1

È necessario verificare se la lettura è riuscita. Suggerimento: non è stato così. Verificare sempre dopo aver letto di leggere con successo l'ingresso:

if (std::cin >> result) { ... } 
if (scanf("%d", result) == 1) { ... } 

In C++ stato di errore è appiccicoso e rimane intorno fino a quando non ottiene clear() ed. Finché il flusso è in stato fallito, non farà nulla di utile. In entrambi i casi, si desidera ignore() il carattere errato o fgetc() esso. Si noti che l'errore potrebbe essere dovuto al raggiungimento della fine del flusso, nel qual caso è impostato eof() oppure EOF viene restituito rispettivamente per iostream o stdio.