2012-03-19 16 views
5

Supongamos que tenemos un menú que presenta al usuario algunas opciones:comportamiento extraño cuando se lee en el int de STDIN

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

El usuario puede presionar de 1 - 4 y luego la tecla enter. El programa realiza esta operación y luego presenta el menú al usuario. Una opción inválida solo debería mostrar el menú nuevamente.

que tienen la main() método siguiente:

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

y la menu() siguientes aparatos:

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 tipos numéricos funciona muy bien. Ingresar 1 - 4 hace que el programa realice la acción deseada, y luego el menú se visualiza de nuevo. Si ingresa un número fuera de este rango, como -1 o 12, se mostrará nuevamente el menú como se esperaba.

Sin embargo, ingresar algo como 'q' simplemente hará que el menú se muestre una y otra vez infinitamente, sin siquiera detenerse para obtener la entrada del usuario.

No entiendo cómo podría estar sucediendo esto. Claramente, se está llamando a menu() ya que el menú se muestra una y otra vez, sin embargo scanf() es parte de menu(), por lo que no entiendo cómo el programa entra en este estado de error donde no se solicita al usuario su entrada.

Originalmente tenía cin >> result que hacía exactamente lo mismo.

Editar: No parece ser un related question, sin embargo el original source code ha desaparecido de Pastebin y uno de los enlaces respuestas a un article que, al parecer, una vez explicado por qué ocurre esto, pero ahora es un vínculo roto. ¿Tal vez alguien puede responder por qué sucede esto en lugar de vincular? :)

Editar: Usando this example, aquí es cómo he resuelto el 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: "; 
    } 
} 

y simplemente sustituye

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

con

int result = getNumericalInput(); 
+0

posible duplicado de [Una simple pregunta sobre cin] (http://stackoverflow.com/questions/5864540/a-simple-question-about-cin) –

Respuesta

6

Cuando intenta para convertir la entrada no numérica a un número, falla y (la parte importante) deja esos datos en el búfer de entrada, por lo que la próxima vez que intente leer un int, seguirá esperando, y volverá a fallar, una y otra vez para siempre.

Hay dos formas básicas de evitar esto. El que prefiero es leer una cadena de datos, luego convertir de eso a un número y tomar la acción adecuada. Normalmente usará std::getline para leer todos los datos hasta la nueva línea y luego tratar de convertirlos. Como leerá cualquier dato que esté esperando, nunca obtendrá "basura" en la entrada.

La alternativa es (especialmente si una conversión falla) utilizar std::ignore para leer los datos de la entrada hasta (normalmente) la siguiente nueva línea.

4

1) Decir esto a ti mismo 1000 veces, o hasta que se queda dormido:


          nunca nunca nunca utilizar las funciones de E/S sin comprobar el valor de retorno.


2) Repetir las anteriores 50 veces.

3) Vuelva a leer su código: ¿Está revisando el resultado de scanf? ¿Qué sucede cuando scanf no puede convertir la entrada en el formato deseado? ¿Cómo harías para aprender esa información si no la conocieras? (Se me ocurren cuatro letras)

También me pregunto por qué usaría scanf en lugar de la operación de iostremas más apropiada, pero eso sufriría exactamente el mismo problema.

+1

Estoy aceptando la respuesta de Jerry porque en realidad me explicó que un error la conversión deja la entrada del usuario en el búfer de entrada, causando que ocurra una y otra vez. Esto no solo me ayuda a mí, sino a todos los que se encuentran con esta pregunta. Además, lo explicó sin ser condescendiente :) – Ozzah

+0

@Ozzah: Claro, no te preocupes. Solo tenga cuidado con E/S en el futuro. –

1

Debe verificar si la lectura fue exitosa. Sugerencia: no fue así. Siempre pruebe después leer que lea con éxito la entrada:

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

En C++ estado fallido es pegajoso y se mantiene alrededor hasta que se pone clear() ed. Siempre que la transmisión esté en estado fallido, no hará nada útil. En cualquier caso, quiere ignore() el caracter malo o fgetc(). Tenga en cuenta que esa falla puede deberse al hecho de haber llegado al final de la secuencia, en cuyo caso se establece eof() o se devuelve EOF para iostream o stdio, respectivamente.