scanf ignorando, bucle infinito

7 minutos de lectura

avatar de usuario
Aviadjo

int flag = 0;
int price = 0;
while (flag==0)
{
    printf("\nEnter Product price: ");
    scanf("%d",&price);
    if (price==0) 
        printf("input not valid\n"); 
    else 
        flag=1;
}

Cuando ingreso un número válido, el ciclo termina como se esperaba. Pero si ingreso algo que no es un número, como hello, entonces el código entra en un bucle infinito. Solo sigue imprimiendo Enter Product price: y input not valid. Pero no espera a que ingrese un nuevo número. ¿Porqué es eso?

  • El compilador convierte tu programa en un ejecutable; eso es todo. No tiene nada que ver con el comportamiento aquí.

    –Jim Balter

    12 de marzo de 2011 a las 23:26

avatar de usuario
crisjj

Cuando ingresas algo que no es un número, scanf fallará y dejará esos caracteres en la entrada. Así que si entras helloscanf verá el h, rechácelo como no válido para un número decimal y déjelo en la entrada. La próxima vez que pase por el bucle, scanf verá el h de nuevo, por lo que sigue en bucle para siempre.

Una solución a este problema es leer una línea completa de entrada con fgets y luego analizar la línea con sscanf. De esa manera, si el sscanf falla, no queda nada en la entrada. El usuario tendrá que ingresar una nueva línea para fgets leer.

Algo a lo largo de estas líneas:

char buffer[STRING_SIZE];
...
while(...) {
    ...
    fgets(buffer, STRING_SIZE, stdin);
    if ( sscanf(buffer, "%d", &price) == 1 )
        break;   // sscanf succeeded, end the loop
    ...
}

Si solo haces un getchar como se sugiere en otra respuesta, entonces es posible que se pierda el \n carácter en caso de que el usuario escriba algo después del número (por ejemplo, un espacio en blanco, posiblemente seguido de otros caracteres).

Siempre debe probar el valor de retorno de sscanf. Devuelve el número de conversiones asignadas, por lo que si el valor devuelto no es el mismo que el número de conversiones solicitadas, significa que el análisis ha fallado. En este ejemplo, hay 1 conversión solicitada, por lo que sscanf devuelve 1 cuando tiene éxito.

avatar de usuario
phwd

El formato %d es para decimales. Cuando scanf falla (se ingresa algo que no sea un decimal), el carácter que provocó que fallara permanecerá como entrada.

Ejemplo.

    int va;
    scanf("%d",&va);
    printf("Val %d 1 \n", val);

    scanf("%d",&va);
    printf("Val %d 2 \n", val);
    return 0;

Por lo tanto, no se produce ninguna conversión.

La función scanf devuelve el valor de la macro EOF si ocurre una falla de entrada antes de cualquier conversión. De lo contrario, la función scanf devuelve el número de elementos de entrada asignados, que puede ser menor que el previsto, o incluso cero, en el caso de una falla temprana de coincidencia.

7.19.6. La función scanf – JTC1/SC22/WG14 – C

Así que debes tener en cuenta que scanf devuelve su propio formulario de notificación para el éxito

int scanf(char *format)

así que también podrías haber hecho lo siguiente

do {
        printf("Enter Product \n");
}
while (scanf("%d", &sale.m_price) == 1);

if(scanf("%d", &sale.m_price) == 0)
        PrintWrongInput();

También manténgase en la parte posterior de su cabeza para tratar de mantenerse alejado de scanf. escanear o escaneo formateado no debe usarse para la entrada interactiva del usuario. Ver las Preguntas Frecuentes C 12.20

  • “Hay un \n en el búfer” ¿Y qué? Excepto por %c, scanf omite los espacios en blanco.

    –Jim Balter

    12 de marzo de 2011 a las 23:23

  • @JimBalter Gracias por avisarme, declaración tonta que hice :(.

    – doctor

    13 de marzo de 2011 a las 0:24

Después del primer número, habrá un ‘\n’ en el búfer de entrada (el retorno que presionó para ingresar el número), por lo que en la segunda iteración la llamada scanf fallará (porque \n no es un número), scanf lo hará no elimine ese \n del búfer, por lo que en la próxima iteración fallará nuevamente y así sucesivamente.

Puede arreglar eso leyendo el ‘\n’ con una llamada getchar() después de scanf.

  • ¿Qué pasa si el usuario escribe algo después del número, antes de presionar regresar? Vea la otra respuesta a continuación.

    – ChrisJ

    12 de marzo de 2011 a las 22:10

  • -1: la especificación de conversión scanf "%d" salta los espacios en blanco al principio de la entrada. si escribes "43\n37" al 1er scanf, leerá y convertirá "43" partida "\n37" en el búfer. El segundo scanf leerá y convertirá eso a 37. Sin duda, hay personajes adicionales en alguna parte, pero no lo son. '\n'.

    – pmg

    12/03/2011 a las 23:00


  • ¿Qué pasa con la avalancha de “respuestas” de personas que no tienen idea de lo que están hablando y no se molestan en verificar sus afirmaciones? ¿Y por qué la gente votó a favor de esta respuesta obviamente incorrecta?

    –Jim Balter

    12 de marzo de 2011 a las 23:22


avatar de usuario
jim balter

Las “respuestas” que dicen que lo hará porque hay un ‘\n’ en el búfer están equivocadas: scanf("%d", ...) salta los espacios en blanco, incluidos los saltos de línea.

Entra en un bucle infinito si x contiene 0 y scanf encuentra un no-número (no sólo un espacio en blanco) o EOF porque x permanecerá en 0 y no hay forma de que se convierta en otra cosa. Esto debería quedar claro con solo mirar su código y pensar en lo que hará en ese caso.

Entra en un bucle infinito porque scanf() no consumirá el token de entrada si falla la coincidencia. scanf() intentará hacer coincidir la misma entrada una y otra vez. necesita vaciar el stdin.

if (!scanf(“%d”, &sale.m_price)) fflush(stdin);

  • fflush(stdin) es un comportamiento indefinido.

    – ilgaar

    22 de enero de 2019 a las 12:27

Editar: cuando escribí esta respuesta por primera vez, era tan estúpido e ignorante acerca de cómo scanf() trabajó.

  • Antes que nada déjame aclarar algo, scanf() no es una función rota, si no sé cómo scanf() funciona y no sé cómo usarlo, entonces probablemente no he leído el manual para scans() y eso no puede ser scanf()es culpa
  • En segundo lugar, para comprender qué está mal con su código, necesita saber cómo scanf() obras.

cuando usas scanf("%d", &price) en su código, el scanf() trata de leer en un integer de la entrada, pero si ingresa un valor no numérico, scanf() sabe que no es el tipo de datos correcto, por lo que vuelve a colocar la entrada de lectura en el búfer, en el siguiente ciclo de ciclo, sin embargo, la entrada no válida todavía está en el búfer, lo que causará scanf() vuelva a fallar porque el búfer no se ha vaciado, y este ciclo continúa para siempre.

Para abordar este problema, puede utilizar el valor de retorno de scanf()que será el número de entradas correctas leídas, sin embargo, debe descartar las entradas no válidas vaciando el búfer para evitar un bucle infinito, el búfer de entrada se vacía cuando el enter se presiona la tecla, puede hacer esto usando el getchar() función para hacer una pausa para obtener una entrada, lo que requerirá que presione el botón enter descartando así la entrada inválida, tenga en cuenta que esto no hará que presione la tecla enter dos veces independientemente de si ingresó o no el tipo de datos correcto, porque el newline character todavía estará en el búfer. Después scanf() ha terminado con éxito la lectura del integer desde la entrada, pondrá \n de vuelta al búfer, así que getchar() lo leerá, pero como no lo necesita, es seguro descartarlo:

#include <stdio.h>

int main(void)
{
    int flag = 0;
    int price = 0;
    int status = 0;
    while (flag == 0 && status != 1)
    {
        printf("\nEnter Product price: ");
        status = scanf("%d", &price);
        getchar();
        if (price == 0) 
            printf("input not valid\n"); 
        else 
            flag = 1;
    }   

    return 0;
}

  • fflush(stdin) es un comportamiento indefinido.

    – ilgaar

    22 de enero de 2019 a las 12:27

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad