C fscanf devolviendo el valor incorrecto

Estoy realmente atascado en algo.

Tengo un archivo de texto, que tiene 1 palabra seguida de ~ 100 números flotantes. Los números flotantes están separados por espacio, tabulador o nueva línea. Este formato se repite varias veces a lo largo del archivo de texto.

Por ejemplo, este es el aspecto del archivo de texto:

one 0.00591 0.07272 -0.78274 ... 0.0673 ... 0.0897 ... two 0.0654 ... 0.07843 ... 0.0873 ... three ... ... ... 

Este es un fragmento de mi código:

 char word[30]; double a[1000]; double j; while (!feof(fp)) { fscanf(fp, "%s", word); printf("%s\n", word); while (!feof(fp) && (fscanf(fp, " %lf", &j)) == 1) { a[z] = j; z++; num_of_vectors++; } z = 0; } 

La palabra “nueve” en el archivo de texto, se imprime como “ne”. Y la palabra “en” ni siquiera se imprime, se imprime un número de punto flotante.

¿Qué estoy haciendo mal?

Cualquier ayuda sería muy apreciada.

Gracias.

Según el estándar:

Un elemento de entrada se define como la secuencia más larga de caracteres de entrada que no excede ningún ancho de campo especificado y que es, o es un prefijo de, una secuencia de entrada coincidente.

La razón probable por la que nine está dando una ne es porque, al leer un valor doble, nan es uno de los valores aceptables. Por lo tanto, la n y i se leen para establecer que no es nan .

Del mismo modo, con la palabra in , es un prefijo válido para inf representa el infinito.

La norma también establece en una nota a pie de página:

fscanf retrocede como máximo un carácter de entrada en el flujo de entrada.

así que es muy posible que esta sea la razón por la que la i en nine no está siendo rechazada.

La conclusión es que, básicamente, no es seguro asumir dónde terminará el puntero del archivo cuando las operaciones de fscanf fallan por algún motivo.


Una forma de solucionar este problema es usar ftell y fseek para guardar el puntero del archivo para cada elemento con éxito, de modo que pueda regresar a la posición correcta si la cosa que está intentando leer no tiene éxito.

Digamos que tienes el archivo de entrada:

 one 1 2 3 4 5 nine 9 8 7 6 5 in 3.14159 2.71828 

El siguiente código guardará y restaurará las posiciones de los archivos para que funcione como desee:

 #include  int main(void) { char buff[50]; double dbl; size_t pos; FILE *fin = fopen("inputFile.txt", "r"); while (fscanf(fin, "%s", buff) == 1) { printf("Got string [%s]\n", buff); pos = ftell(fin); while (sscanf(buff, "%lf", &dbl) == 1) { printf("Got double [%f]\n", dbl); pos = ftell(fin); } fseek(fin, pos, SEEK_SET); } fclose(fin); return 0; } 

Al comentar el fseek , puede ver un comportamiento similar al que describe:

 Got string [one] Got double [1.000000] Got double [2.000000] Got double [3.000000] Got double [4.000000] Got double [5.000000] Got string [ne] Got double [9.000000] Got double [8.000000] Got double [7.000000] Got double [6.000000] Got double [5.000000] Got double [3.141590] Got double [2.718280] 

Considero que esta solución es un poco desordenada, ya que continuamente se debe tener que llamar a ftell y ocasionalmente fseek para que funcione.


Otra forma es simplemente leer todo como cadenas y decidir si es una operación numérica o de cadena con sscanf después de leerlo, como en el siguiente código (con el archivo de entrada mencionado anteriormente):

 #include  int main(void) { char buff[50]; double dbl; FILE *fin = fopen("inputFile.txt", "r"); while (fscanf(fin, "%s", buff) == 1) { if (sscanf(buff, "%lf", &dbl) == 1) { printf("Got double [%f]\n", dbl); } else { printf("Got string [%s]\n", buff); } } fclose(fin); return 0; } 

Esto funciona porque un valor de punto flotante es realmente un subconjunto adecuado de una cadena (es decir, no tiene espacios incrustados).


El resultado de ambos progtwigs anteriores es:

 Got string [one] Got double [1.000000] Got double [2.000000] Got double [3.000000] Got double [4.000000] Got double [5.000000] Got string [nine] Got double [9.000000] Got double [8.000000] Got double [7.000000] Got double [6.000000] Got double [5.000000] Got string [in] Got double [3.141590] Got double [2.718280] 

que es básicamente lo que se deseaba.


Una cosa que debe tener en cuenta es que el escaneo de algo así como inf o nan como doble funcionará, es el comportamiento previsto de la biblioteca (y cómo habría funcionado su código original si no hubiera tenido los problemas). Si eso no es aceptable, puede hacer algo como evaluar la cadena antes de intentar escanearla como doble, para asegurarse de que no sea uno de esos valores especiales.