Usando fscanf () vs. fgets () y sscanf ()

En el libro Practical C Programming, encuentro que la combinación de fgets() y sscanf() se usa para leer entradas. Sin embargo, me parece que el mismo objective se puede cumplir más fácilmente usando solo la fscanf() :

Del libro (la idea, no el ejemplo):

 int main() { int age, weight; printf("Enter age and weight: "); char line[20]; fgets(line, sizeof(line), stdin); sscanf(line, "%d %d", &age, &weight); printf("\nYou entered: %d %d\n", age, weight); return 0; } 

Cómo creo que debería ser:

 int main() { int age, weight; printf("Enter age and weight: "); fscanf(stdin, "%d %d", &age, &weight); printf("\nYou entered: %d %d\n", age, weight); return 0; } 

¿O hay alguna peculiaridad oculta que me estoy perdiendo?

Hay algunas diferencias de comportamiento en los dos enfoques. Si usa fgets() + sscanf() , debe ingresar ambos valores en la misma línea, mientras que fscanf() en stdin (o de manera equivalente, scanf() ) los leerá de diferentes líneas si no encuentra el segundo valor en la primera línea que ingresaste.

Pero, probablemente, las diferencias más importantes tienen que ver con el manejo de los casos de error y la mezcla de entrada orientada a la línea y entrada orientada al campo.

Si lees una línea que no puedes analizar con sscanf() después de haberla leído usando fgets() tu progtwig simplemente puede descartar la línea y seguir adelante. Sin embargo, fscanf() , cuando no puede convertir campos, deja toda la entrada en la secuencia. Por lo tanto, si no pudo leer la entrada que quería, tendría que ir a leer todos los datos que desea ignorar.

El otro gotcha sutil viene si desea mezclar llamadas orientadas en campo (ala scanf() ) con líneas orientadas (por ejemplo, fgets() ) en su código. Cuando scanf() convierte un int por ejemplo, dejará atrás un \n en el flujo de entrada (suponiendo que haya uno, como al presionar la tecla enter), lo que provocará que una llamada posterior a fgets() regrese inmediatamente con solo Ese personaje en la entrada. Este es un problema muy común para los nuevos progtwigdores.

Entonces, si bien tienes razón en que puedes usar fscanf() esa manera, puedes evitar algunos dolores de cabeza usando fgets() + sscanf() .

El problema con solo usar fscanf() es, principalmente, en la administración de errores.

Imagina que ingresas ” 51 años, 85 Kg ” en ambos progtwigs.

El primer progtwig falla en sscanf() y aún tiene la line para informar errores al usuario, para intentar una alternativa de análisis diferente, a algo ;

El segundo progtwig falla a los años , la age es utilizable, el weight es inutilizable.

Recuerde verificar siempre el valor de retorno de *scanf() para verificar errores.

  fgets(line, sizeof(line), stdin); if (sscanf(line, "%d%d", &age, &weight) != 2) /* error with input */; 

Editar

Con su primer progtwig, después del error, el búfer de entrada está libre; con el segundo progtwig el buffer de entrada comienza con AÑO …

La recuperación en el primer caso es fácil; La recuperación en el segundo caso tiene que pasar por algún tipo de borrado del búfer de entrada.

No hay diferencia entre fscanf() y fgets() / sscanf() cuando:

  1. Los datos de entrada están bien formados.

Se producen dos tipos de errores: E / S y formato. fscanf() maneja simultáneamente estos dos tipos de error en una función pero ofrece pocas opciones de recuperación. Los fgets() y sscanf() separados permiten la separación lógica de los problemas de E / S de los formatos y, por lo tanto, una mejor recuperación.

  1. Sólo 1 ruta de análisis.

Separar la E / S del escaneo permite múltiples opciones de sscanf() . Si un escaneo dado de un búfer no logra los resultados deseados, otros sscanf() con diferentes formatos están disponibles.

  1. No incrustado '\0' .

Rara vez ocurre '\0' , pero si ocurre uno, sscanf() no lo verá ya que el escaneo se detiene con su aparición, mientras que fscanf() continúa.

En todos los casos, verifique los resultados de las tres funciones.