2008-10-19 30 views
10

He estado teniendo muchos problemas tratando de encontrar la manera de usar scanf(). Parece funcionar bien con números enteros, siendo bastante sencillo scanf("%d", &i).Analizando entrada con scanf en C

Donde me encuentro con problemas está utilizando scanf() en bucles tratando de leer la entrada. Por ejemplo:

do { 
    printf("counter: %d: ", counter); 
    scanf("%c %c%d", &command, &prefix, &input); 
} while (command != 'q'); 
  1. Cuando entro en una entrada estructurada de forma válida como c P101, que parece bucle de nuevo antes de que me provocó. Esto parece suceder incluso con un solo:

    scanf("%c", &c) 
    

    en un ciclo while. Hará el ciclo dos veces antes de avisarme nuevamente. ¿Qué lo hace girar dos veces y cómo lo detengo?

  2. Cuando ingreso menos cantidad de datos de entrada que programáticamente no tendrían otro carácter o número como q, presionar enter parece indicarme que ingrese más. ¿Cómo obtengo scanf() para procesar entradas de caracteres individuales y dobles?

Respuesta

20

Cuando ingresa "c P101", el programa recibe realmente "c P101\n". La mayoría de los especificadores de conversión omiten espacios en blanco iniciales, incluidas las líneas nuevas, pero %c no. La primera vez en todo hasta que se lea "\n", la segunda vez que se lee "\ n" en command, "c" se lee en prefix, y "P" queda, que no es un número, por lo que la conversión falla y "P101\n" se deja en la transmisión. La siguiente vez que se almacena "P" en el comando, "1" se almacena en el prefijo y 1 (del "01" restante) se almacena en la entrada con "\n" todavía en la transmisión la próxima vez. Puede solucionar este problema colocando un espacio al principio de la cadena de formato que omitirá cualquier espacio en blanco inicial, incluidas las líneas nuevas.

Algo similar está sucediendo en el segundo caso, cuando se introduce "q", "q\n" se introduce en la corriente, la primera vez alrededor de la "q" se lee, la segunda vez que el "\n" se lee , solo en la tercera llamada es la segunda lectura "q", puede evitar el problema de nuevo agregando un carácter de espacio al principio de la cadena de formato.

Una mejor forma de hacer esto sería usar algo como fgets() para procesar una línea a la vez y luego usar sscanf() para realizar el análisis sintáctico.

+2

Muy buena explicación, sospeché que debe estar leyendo \ n, pero esto ciertamente complicó lo que pensé que era un simple análisis. Buscaré en fgets y sscanf(). – zxcv

+0

Consideraría agregar un párrafo sobre verificar el valor de retorno de 'scanf()' para asegurarse de que obtuvo lo que esperaba. A menos que sepa de una pregunta anterior, es probable que se convierta en la pregunta y respuesta canónicas para este problema, pero el la respuesta canónica debe cubrir todos los puntos razonables y creo que el retorno de 'scanf()' es un punto razonable.Haz un comentario dirigido a mí cuando lo hagas; A continuación, eliminaré este comentario y marcaré su comentario para eliminarlo (o puede marcarlo como obsoleto, pero me gustaría saber si realizó los cambios). Gracias. –

1

Para la pregunta 1, sospecho que usted tiene un problema con su printf(), ya que no hay terminación "\ n".

El comportamiento predeterminado de printf es almacenar el buffer hasta que tenga una línea completa. Eso es a menos que cambie explícitamente el almacenamiento en búfer en stdout.

Para la pregunta 2, acaba de golpear uno de los mayores problemas con scanf(). A menos que su entrada coincida exactamente con la cadena de exploración que ha especificado, sus resultados no serán los esperados.

Si tiene una opción, obtendrá mejores resultados (y menos problemas de seguridad) ignorando scanf() y haciendo su propio análisis sintáctico. Por ejemplo, use fgets() para leer una línea completa en una cadena, y luego procese los campos individuales de la cadena — incluso utilizando sscanf().

+0

Hmm, no creo que sea eso. incluso con un \ n puesto, simplemente lo imprimirá dos veces antes de que scanf me vuelva a preguntar. Incluso traté de poner printf antes y después del scanf (dentro del ciclo do-while) y se repiten dos veces antes de que Scanf vuelva a preguntar. Esto es con entrada de un solo carácter, como s y retorno. – zxcv

-1

scanf apesta, por las razones que ya ha descubierto. Es mucho mejor usar algo para obtener toda la línea y luego analizarla.

+0

Estoy abierto a sugerencias ... ¿cuál es la mejor alternativa? – zxcv

+0

Usa qué? ¿Dice que algo apesta pero no ofrece alternativas? – Alfred

+0

fgets es la solución más simple. Pero hay bibliotecas C que hacen esto de forma segura. –

1

¡Está realmente roto! No sabía que

#include <stdio.h> 

int main(void) 
{ 
    int counter = 1; 
    char command, prefix; 
    int input; 

    do 
    { 
     printf("counter: %d: ", counter); 
     scanf("%c %c%d", &command, &prefix, &input); 
     printf("---%c %c%d---\n", command, prefix, input); 
     counter++; 
    } while (command != 'q'); 
} 
counter: 1: a b1 
---a b1--- 
counter: 2: c d2 
--- 
c1--- 
counter: 3: e f3 
---d 21--- 
counter: 4: ---e f3--- 
counter: 5: g h4 
--- 
g3--- 

La salida parece encajar con la respuesta de Robert.

0

Tal vez usando un bucle while, no un bucle do ... while ayudará. De esta forma, la condición se prueba antes de la ejecución del código. Prueba el siguiente fragmento de código:

while(command != 'q') 
    { 
     //statements 
    } 

También, si se conoce la longitud de la cadena antes de tiempo, 'para' bucles pueden ser mucho más fácil de trabajar que 'mientras que' bucles. También hay algunas formas más difíciles de determinar dinámicamente la longitud también.

Como una queja final: scanf() no "chupa". Hace lo que hace y eso es todo. fgets() es muy peligroso (aunque conveniente para aplicaciones sin riesgo), ya que no realiza ninguna comprobación de la entrada de forma nativa. Es MUY comúnmente conocido como un punto de explotación, específicamente ataques de desbordamiento de búfer, sobreescribiendo espacio en registros no asignados para esa variable. Por lo tanto, si elige usarlo, dedique un tiempo a realizar comprobaciones/correcciones de errores sólidos.

Espero que esto ayude a alguien en el futuro ya que supongo que ya lo ha manejado. ;)

+0

¿Por qué dices 'fgets' es peligroso? Tal vez lo estás confundiendo con 'get'? – interjay

+0

mi error, me refería a 'get()', codificación de última hora! ... – jap3r