Cuando trabajamos con la función scanf en C, descubrimos que su uso va mucho más allá de simplemente leer valores básicos como enteros o caracteres. Una de las características más interesantes es que podemos incluir en el formato cadenas fijas que scanf espera encontrar exactamente en la entrada. Esto nos permite leer datos con formatos complejos y estructurados, como archivos con líneas que siguen un patrón específico.
Por ejemplo, imaginemos que tenemos un archivo llamado mediciones.txt donde cada línea comienza con la palabra in, seguida de una fecha en formato año-mes-día, luego el nombre de una ciudad y finalmente una medición de temperatura. Podemos definir variables para almacenar el año, mes, día, la ciudad y la temperatura, y luego usar un único scanf con un formato que incluya tanto las cadenas fijas como los placeholders para extraer todos esos datos de una sola lectura.
El formato podría ser algo así:
scanf("in %d-%d-%d %s %f", &anio, &mes, &dia, ciudad, &temperatura);
Aquí, scanf espera encontrar literalmente la palabra in seguida de un espacio, luego un año, un guion, un mes, otro guion, un día, un espacio, una palabra para la ciudad y finalmente un número flotante para la temperatura. Si la entrada no cumple con este formato exacto, scanf no leerá nada y dejará las variables sin modificar.
Es importante destacar que esta técnica funciona bien cuando la ciudad es una sola palabra. Si la ciudad tuviera espacios, como Santiago de Chile, este método no sería suficiente, ya que %s solo lee hasta el siguiente espacio.
Otro aspecto crucial al usar scanf es cómo maneja los errores. Si esperamos un número y el usuario introduce una letra, scanf no consume esa entrada inválida, sino que la deja en el buffer de entrada. Esto puede provocar problemas, especialmente en bucles donde esperamos que el usuario introduzca una opción válida.
Por ejemplo, en un menú típico donde pedimos al usuario que seleccione una opción entre 1 y 5, podemos tener un bucle que repite la lectura mientras la opción no esté en ese rango. Sin embargo, si el usuario introduce un carácter no numérico, scanf no lo consume y lo deja en el buffer, lo que hace que el siguiente intento de lectura vuelva a fallar con el mismo carácter, generando un bucle infinito.
La razón de este comportamiento es que scanf utiliza internamente una función similar a ungetc, que permite devolver caracteres al buffer de entrada si no le gustan. Así, cuando encuentra un carácter que no encaja con el formato esperado, lo devuelve al buffer para que la próxima lectura lo encuentre de nuevo.
Para evitar este problema, debemos limpiar el buffer de entrada después de un error en scanf. Una forma portátil y efectiva de hacerlo es leer y descartar caracteres hasta encontrar el salto de línea que indica el final de la entrada del usuario. Esto se puede hacer con un simple bucle que llame a getchar hasta encontrar el carácter '\n':
while (getchar() != '\n');
Este código consume todos los caracteres restantes en la línea, incluyendo el salto de línea, dejando el buffer limpio para la siguiente lectura.
Es importante no confiar en funciones como fflush(stdin), que aunque funcionan en Windows, no son portables y pueden causar comportamientos inesperados en otros sistemas operativos.
En definitiva, scanf es una herramienta poderosa que nos permite leer datos con formatos complejos y realizar múltiples lecturas en una sola llamada. Sin embargo, su manejo de errores y del buffer de entrada requiere cuidado para evitar problemas como bucles infinitos o lecturas incorrectas. Por eso, cuando sea posible, es recomendable usar alternativas más robustas como readline o librerías especializadas que gestionan mejor estos casos y nos evitan tener que lidiar con estos detalles manualmente.