2010-01-27 16 views
5

gcc 4.4.2Usando scanf para aceptar la entrada del usuario

Estaba leyendo un artículo sobre scanf. Personalmente, nunca revisé el código de retorno de un scanf.

#include <stdio.h> 

int main(void) 
{ 
    char buf[64]; 
    if(1 == scanf("%63s", buf)) 
    { 
    printf("Hello %s\n", buf); 
    } 
    else 
    { 
    fprintf(stderr, "Input error.\n"); 
    } 
    return 0; 
} 

Me pregunto qué otras técnicas experimentan los programadores cuando utilizan scanf cuando quieren obtener la entrada del usuario? ¿O usan otra función o escriben la suya?

Gracias por cualquier sugerencia,

EDITAR =========

#include <stdio.h> 

int main(void) 
{ 
    char input_buf[64] = {0}; 
    char data[64] = {0}; 

    printf("Enter something: "); 
    while(fgets(input_buf, sizeof(input_buf), stdin) == NULL) 
    { 
    /* parse the input entered */ 
    sscanf(input_buf, "%s", data); 
    } 

    printf("Input [ %s ]\n", data); 

    return 0; 
} 

creo que la mayoría de los programadores de acuerdo en que scanf es malo, y la mayoría están de acuerdo en usar fgets y sscanf. Sin embargo, puedo usar los fgets para leer la entrada. Sin embargo, si no sé en qué ingresará el usuario, ¿cómo puedo saber qué analizar? Por ejemplo, ¿como si el usuario ingresara su dirección que contendría números y caracteres y en cualquier orden?

+3

Nunca verificando el código de retorno de cualquiera de las rutinas de entrada siempre es dudoso en extremo, debe ser afortunado. La respuesta corta es "No uso' scanf() 'o' fscanf() '; de vez en cuando uso' sscanf() 'pero pisa con cautela". –

+0

Después de actualizar mi respuesta. ¿Cómo usaría normalmente sscanf para analizar los datos? Si no está seguro de qué datos va a ingresar el usuario? Gracias. – ant2009

Respuesta

6

No utilice scanf directamente. Es sorprendentemente difícil de usar. Es mejor leer una línea completa de entrada y luego analizarla (posiblemente con sscanf).

Leer esta entrada (y las entradas que hace referencia) de la FAQ comp.lang.c: http://c-faq.com/stdio/scanfprobs.html

Editar: bien, para hacer frente a su pregunta adicional de su propia edición: Si permite que la entrada no estructurada, entonces tendrás que intentar analizar la cadena de múltiples maneras hasta que encuentres una que funcione. Si no puede encontrar una coincidencia válida, entonces debe rechazar la entrada y solicitar al usuario nuevamente, probablemente explicando en qué formato desea que esté la entrada.

Para algo más complicado, probablemente sea mejor que utilizando una biblioteca de expresiones regulares o incluso usando kits de herramientas lexer/parser dedicados (por ejemplo, flex y bison).

+0

Hola, he actualizado mi respuesta. Pero no estoy seguro sobre el análisis usando sscanf? Gracias. – ant2009

2

scanf() tiene problemas, ya que si se espera que un usuario escriba un número entero y escriba una cadena, a menudo el programa se bombardea. Esto puede superarse leyendo todas las entradas como una cadena (use getchar()), y luego convirtiendo la cadena al tipo de datos correcto.

/* example one, to read a word at a time */ 
#include <stdio.h> 
#include <ctype.h> 
#define MAXBUFFERSIZE 80 

void cleartoendofline(void); /* ANSI function prototype */ 

void cleartoendofline(void) 
{ 
    char ch; 
    ch = getchar(); 
    while(ch != '\n') 
     ch = getchar(); 
} 

main() 
{ 
    char ch;      /* handles user input */ 
    char buffer[MAXBUFFERSIZE]; /* sufficient to handle one line */ 
    int  char_count;    /* number of characters read for this line */ 
    int  exit_flag = 0; 
    int  valid_choice; 

    while(exit_flag == 0) { 
     printf("Enter a line of text (<80 chars)\n"); 
     ch = getchar(); 
     char_count = 0; 
     while((ch != '\n') && (char_count < MAXBUFFERSIZE)) { 
      buffer[char_count++] = ch; 
      ch = getchar(); 
     } 
     buffer[char_count] = 0x00;  /* null terminate buffer */ 
     printf("\nThe line you entered was:\n"); 
     printf("%s\n", buffer); 

     valid_choice = 0; 
     while(valid_choice == 0) { 
      printf("Continue (Y/N)?\n"); 
      scanf(" %c", &ch); 
      ch = toupper(ch); 
      if((ch == 'Y') || (ch == 'N')) 
       valid_choice = 1; 
      else 
       printf("\007Error: Invalid choice\n"); 
      cleartoendofline(); 
     } 
     if(ch == 'N') exit_flag = 1; 
    } 
} 
1

Hago una llamada de bucle hasta que se lee el final de la línea, y luego llamo a sscanf para analizar los datos. Es una buena idea comprobar si sscanf llega al final de la línea de entrada.

1

Raramente utilizo scanf. La mayoría de las veces, uso fgets() para leer los datos como una cadena. Luego, dependiendo de la necesidad, puedo usar sscanf(), u otras funciones como la familia de funciones strto*, str*chr(), etc., para obtener datos de la cadena.

Si uso scanf() o fgets() + sscanf(), siemprecomprobar los valores de retorno de las funciones para asegurarse de que hice lo que quería que hicieran. Tampoco uso strtok() para tokenizar cadenas, porque creo que la interfaz de strtok() está rota.

5

No uso scanf() para la entrada de usuario interactiva; Leo todo como texto usando fgets(), luego analizo la entrada según sea necesario, usando strtol() y strtod() para convertir texto a valores numéricos.

Un ejemplo de donde scanf() cae es cuando el usuario introduce un mal valor numérico, pero la parte inicial de la misma es válida, algo así como lo siguiente:

if (scanf("%d", &num) == 1) 
{ 
    // process num 
} 
else 
{ 
    // handle error 
} 

Si el usuario escribe en "12e4" , scanf() convertirá con éxito y asignará el "12" a num, dejando "e4" en la secuencia de entrada para ensuciar una lectura futura. La entrada entera debe tratarse como falsa, pero scanf() no puede detectar ese tipo de error. Otoh, si hago algo como:

if (fgets(buffer, sizeof buffer, stdin)) 
{ 
    int val; 
    char *chk; 
    val = (int) strtol(buffer, &chk, 10); 
    if (!isspace(*chk) && *chk != 0) 
    { 
    // non-numeric character in input; reject it completely 
    } 
    else 
    { 
    // process val 
    } 
} 

que puede detectar el error en la entrada y rechazarlo antes de usar cualquier parte de ella. Esto también hace un mejor trabajo al no dejar basura en la corriente de entrada.

scanf() es una gran herramienta si puede garantizar que su entrada siempre esté bien formada.

+0

Hola. Digamos que quería que el usuario ingrese su dirección. Que tendría números y letras. Podrías leer en la entrada usando fgets. Pero cómo analizarías la entrada. Creo que podrías leerlo en un búfer usando fgets y eso sería todo. No puedo ver cómo usarías sscanf para esto. Gracias. – ant2009

+0

Trataría una dirección postal como todo el texto, posiblemente dividida en varios campos (dirección, nombre de la calle, ciudad, estado, código postal, etc.). Cuándo y cómo me gustaría tokenize dependerá de la aplicación (como si tuviera que buscar registros basados ​​en el código postal o el nombre de la calle o algo así). Si todo lo que tengo que hacer es almacenarlo para mostrarlo más tarde, probablemente ni siquiera me molestaría en hacerlo. –

Cuestiones relacionadas