¿Cuál es la mejor manera de obtener información del usuario en C?

Mucha gente dijo que scanf no debería usarse en un “progtwig más serio”, como con getline .

Empecé a perderme: si cada función de entrada que recibía a través de la gente decía que no debería usar ninguna de ellas, ¿qué debería usar? ¿Hay una manera más “estándar” de obtener información que no conozco?

En general, fgets() se considera una buena opción. Lee líneas enteras en un buffer, y desde allí puedes hacer lo que necesites. Si desea un comportamiento como scanf() , puede pasar las cadenas que lee a lo largo de sscanf() .

La principal ventaja de esto es que si la cadena no se puede convertir, es fácil recuperarla, mientras que con scanf() le queda la entrada en stdin que necesita drenar. Además, no terminará en la trampa de mezclar la entrada orientada a la línea con scanf() , lo que causa dolores de cabeza cuando cosas como \n quedan en stdin comúnmente llevando a los nuevos codificadores a creer que las llamadas de entrada se han ignorado por completo.

Algo como esto podría ser de su agrado:

 char line[256]; int i; if (fgets(line, sizeof(line), stdin)) { if (1 == sscanf(line, "%d", &i)) { /* i can be safely used */ } } 

Arriba debe tener en cuenta que fgets() devuelve NULL en EOF o error, por lo que lo envolví en un if . La llamada sscanf() devuelve la cantidad de campos que se convirtieron correctamente.

Tenga en cuenta que fgets() puede no leer una línea completa si la línea es más grande que su buffer, lo que en un progtwig “serio” es algo que debería considerar.

Para una entrada simple donde puede establecer un límite fijo en la longitud de entrada, recomendaría leer los datos del terminal con fgets() .

Esto se debe a que fgets() te permite especificar el tamaño del búfer (a diferencia de gets() , que por esta misma razón nunca debe usarse para leer entradas de humanos):

 char line[256]; if(fgets(line, sizeof line, stdin) != NULL) { /* Now inspect and further parse the string in line. */ } 

Recuerde que retendrá, por ejemplo, los caracteres de alimentación de línea, lo que podría ser sorprendente.

ACTUALIZACIÓN : como se señala en un comentario, hay una mejor alternativa si está bien con la obtención de la responsabilidad de rastrear la memoria: getline() . Esta es probablemente la mejor solución de propósito general para el código POSIX, ya que no tiene ningún límite estático en la longitud de las líneas para leer.

Hay varios problemas con el uso de scanf :

  • leer texto con un especificador de conversión simple %s tiene el mismo riesgo que con el uso de gets() ; si el usuario escribe una cadena que es más larga de lo que el búfer de destino tiene el tamaño para contener, obtendrá un desbordamiento del búfer;

  • si usa %d o %f para leer datos numéricos, ciertos patrones incorrectos no pueden capturarse y rechazarse por completo; si está leyendo un número entero con %d y el usuario escribe ” 12r4 “, scanf convertirá y asignará los 12 mientras se va r4 en el flujo de entrada para ensuciar la siguiente lectura;

  • algunos especificadores de conversión omiten espacios en blanco adelantados, otros no, y el hecho de no tenerlo en cuenta puede ocasionar problemas donde se omite por completo la entrada;

Básicamente, se requiere mucho esfuerzo adicional para leer a prueba de balas usando scanf .

Una buena alternativa es leer todas las entradas como texto usando fgets() , y luego tokenizar y convertir la entrada usando sscanf o combinaciones de strtok , strtol , strtod , etc.

Usa fgets para obtener los datos y usa sscanf (u otro método) para interpretarlos.

Consulte esta página para saber por qué es mejor usar fgets + sscanf lugar de scanf

http://c-faq.com/stdio/scanfprobs.html