Estoy creando una aplicación de línea de comandos con un aviso. Funciona bien siempre que la entrada quepa dentro del búfer, pero cuando la entrada es más grande, obtengo un comportamiento extraño. A continuación se muestra un ejemplo mínimo simplificado que tiene el mismo error.
int main() { for (;;) { const int size = 8; char str[size]; printf("> "); fgets(str, size, stdin); toupper_str(str); printf("Result: %s\n", str); } } Funciona bien si la entrada es más pequeña que size .
> asdf Result: ASDF > Cuando la entrada es más grande, la parte de la entrada en el rango se procesa y, en la siguiente iteración del bucle, fgets inmediatamente el resto de la entrada dada. Esto lleva a que esa parte de la entrada también se procese y a una salida extraña.
> asdfghjk Result: ASDFGHJ > Result: K > Puedo ver si la entrada es mayor o igual al tamaño comparando el último carácter con una nueva línea. fgets retiene la nueva línea mientras quepa.
fgets(str, size, stdin); if (str[strlen(str) - 1] != '\n') { fprintf(stderr, "Input too long\n"); }Cuando se detecta esto, ¿cómo evito que lea el resto de la entrada demasiado larga en la siguiente iteración?
He visto preguntas similares aquí, pero ninguna que haga la misma pregunta.
¿Cómo evito que lea el resto de la entrada demasiado larga en la siguiente iteración?
El código necesita 1) detectar si la entrada es "demasiado larga" 2) consumir la entrada adicional.
fgets() no sobrellenará el búfer. Si llena el búfer, el último char en el búfer es '\0' . Así que configúralo como no '\0' antes de leer. Luego, el código sabe si se llenó todo el búfer. Luego verifique si el char anterior era un '\n' . Si no es un final de línea, puede existir un char adicional en stdin
char str[100]; // Insure buffer is at least size 2 for (;;) { str[sizeof str - 1] = `x`; if (fgets(str, size, stdin) == NULL) { // No more to read or IO error break; } int extra_data_found = 0; if (str[sizeof str - 1] == '\0' && str[sizeof str - 2] != '\n') { // Cope with potential extra data in `stdin`: read and toss int ch; while ((ch = fgetc(stdin)) != '\n' && ch != EOF) { extra_data_found = 1; } } // Use `str` as needed, noting if additional unsaved data found foo(str, extra_data_found); } Nota: en un error de archivo, fgets() devuelve NULL y el contenido de str no está definido.
Nota: En lugar de str[sizeof str - 1] == '\0' , el código podría usar strlen(str) == sizeof str - 1 . Esto se engaña si fgets() lee un carácter nulo '\0' .
Casos de esquina:
1. La str típica tendrá hasta 98 char y luego '\n' y '\0' . ¿Está bien si la última str tiene 99 char y luego '\0' ?
2. Si #1 está bien, ¿entonces una str típica puede tener 99 char y luego '\0' ?
Cuando la entrada es demasiado larga, debe leer los caracteres restantes en la entrada estándar antes de continuar con la siguiente iteración del ciclo.
if (fgets(str, size, stdin) == NULL) { if (feof(stdin)) { return 0; else { perror("Could not read from stdin"); exit(1); } } else if (strchr(str, '\n') == NULL) { int c; while((c = getc(stdin)) != '\n' && c != EOF); fprintf(stderr, "Input too long\n"); } Si está en un sistema POSIX como OS X o Linux, ya existe la función getline que lee una cadena terminada en una nueva línea de longitud arbitraria de una secuencia. También puede encontrar muchas versiones gratuitas/de código abierto de esta función en línea.