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


Mucha gente dijo que scanf no debería usarse en "programa más serio", igual que con getline.

Empecé a perderme: si cada función de entrada que obtuve a través de la gente decía que no debía usar ninguna de ellas, entonces ¿qué debería usar? ¿Hay una forma más" estándar " de obtener información de la que no soy consciente?

Author: nbro, 2012-02-14

4 answers

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

La principal ventaja de esto, es que si la cadena no se convierte, es fácil de recuperar, mientras que con scanf() te quedas con la entrada en stdin que necesita drenar. Además, no terminarás en la trampa de mezclar entradas orientadas a la línea con scanf(), lo que causa dolores de cabeza cuando cosas como \n se dejan en stdin comúnmente llevan a los nuevos codificadores a creer que las llamadas de entrada habían sido ignoradas 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 el número de campos que se han convertido correctamente.

Tenga en cuenta que fgets() no puede leer una línea completa si la línea es más grande que su búfer, que en un programa "serio" es ciertamente algo que usted debe considerar.

 35
Author: FatalError,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-14 14:11:06

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

Esto se debe a que fgets() le permite especificar el tamaño del búfer (a diferencia de gets(), que por esta misma razón debería prácticamente nunca usarse para leer la entrada de los 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, el carácter(s) de linefeed, lo que podría ser sorprendente.

ACTUALIZACIÓN: Como se señaló en un comentario, hay una mejor alternativa si estás de acuerdo con asumir 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 a leer.

 11
Author: unwind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-14 14:29:48

Hay varios problemas con el uso de scanf:

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

  • Si usa %d o %f para leer la entrada numérica, ciertos patrones defectuosos no se pueden detectar y rechazar por completo -- si está leyendo un entero con %d y el usuario escribe "12r4", scanf convertirá y asignará el 12 mientras deja r4 en el flujo de entrada para estropear la siguiente lectura;

  • Algunos especificadores de conversión omiten los espacios en blanco iniciales, otros no, y el hecho de no tenerlo en cuenta puede dar lugar a problemas en los que se omite por completo alguna entrada;

Básicamente, se necesita un lote de esfuerzo adicional para leer a prueba de balas usando scanf.

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

 5
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-14 14:19:00

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

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

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

 2
Author: ouah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-14 14:12:58