2009-05-14 32 views
6

Estoy intentando leer una línea utilizando el código siguiente problema:leer una línea usando fscanf()

while(fscanf(f, "%[^\n\r]s", cLine) != EOF) 
{ 
    /* do something with cLine */ 
} 

Pero de alguna forma consigo sólo la primera línea en todo momento. ¿Es esta una mala forma de leer una línea? ¿Qué debería arreglar para que funcione como se esperaba?

Respuesta

0

Me parece que está tratando de usar operadores de expresiones regulares en su secuencia fscanf. La cadena [^\n\r] no significa nada para fscanf, por lo que su código no funciona como se esperaba.

Además, fscanf() no devuelve EOF si el elemento no coincide. Más bien, devuelve un número entero que indica el número de coincidencias, que en su caso probablemente sea cero. EOF solo se devuelve al final de la transmisión o en caso de error. Entonces, lo que está sucediendo en su caso es que la primera llamada a fscanf() se lee hasta el final del archivo buscando una cadena coincidente, luego devuelve 0 para hacerle saber que no se encontró ninguna coincidencia. La segunda llamada devuelve EOF porque se ha leído todo el archivo.

Finalmente, tenga en cuenta que el operador de formato% s scanf solo captura el siguiente carácter de espacio en blanco, por lo que no necesita excluir \ n o \ r en ningún caso.

consulte la documentación fscanf para más información: http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/

+1

[^ a-z] realmente excluye a-z en scanf. Aunque, la cadena como se indicó anteriormente, busca "un par de caracteres, el primero no es un salto de línea, y el segundo es un" – Tordek

+0

La documentación de fscanf en cplusplus.com está incompleta. Google 'fscanf scanset'. – Dingo

19

es casi siempre una mala idea usar la función fscanf() ya que puede dejar el puntero del archivo en un lugar desconocido en caso de fallo.

Prefiero usar fgets() para obtener cada línea y luego sscanf() que. Luego puede continuar examinando la lectura de línea como mejor le parezca. Algo así como:

#define LINESZ 1024 
char buff[LINESZ]; 
FILE *fin = fopen ("infile.txt", "r"); 
if (fin != NULL) { 
    while (fgets (buff, LINESZ, fin)) { 
     /* Process buff here. */ 
    } 
    fclose (fin); 
} 

fgets() parece ser lo que estás tratando de hacer, leer en una cuerda hasta que se encuentra con un carácter de nueva línea.

+0

¿Cómo podría usar la función sscanf para leer solo una línea (BTY es 1024 del tamaño de una línea?) gracias! –

+1

fgets lee una línea "o menos". fgets (buffer, 1024, file) leerá una línea, tanto como haya en el archivo, o 1024 caracteres. Si lee una línea completa, entonces búfer [strlen (buffer)] == '\ n'. Si llega a EOF, devuelve nulo, y de lo contrario, hay más texto en la línea. – Tordek

+0

http://stackoverflow.com/questions/865335/why-is-it-a-bad-idea-to-use-the-fscanf-function –

0

Su ciclo tiene varios problemas. Usted escribió:

while(fscanf(f, "%[^\n\r]s", cLine) != EOF) 
    /* do something */; 

Algunas cosas a considerar:

  1. fscanf() devuelve el número de elementos almacenados. Puede devolver EOF si lee más allá del final del archivo o si el identificador del archivo tiene un error. Debe distinguir una devolución válida de cero, en cuyo caso no hay contenido nuevo en el almacenamiento intermedio cLine de una lectura correcta.

  2. Tiene un problema cuando falla la coincidencia porque es difícil predecir hacia dónde apunta el archivo en la transmisión. Esto hace que la recuperación de un partido fallido sea más difícil de lo que podría esperarse.

  3. El patrón que escribió probablemente no hace lo que pretendía. Está haciendo coincidir cualquier número de caracteres que no son CR o LF, y luego espera encontrar un literal s.

  4. No ha protegido su memoria intermedia de un desbordamiento. Se puede leer cualquier cantidad de caracteres del archivo y escribir en el búfer, independientemente del tamaño asignado a ese búfer. Este es un error desafortunadamente común, que en muchos casos puede ser explotado por un atacante para ejecutar código arbitrario de los atacantes que elijan.

  5. A menos que haya solicitado específicamente que f se abra en modo binario, la traducción de final de línea se realizará en la biblioteca y, en general, nunca verá caracteres CR, y generalmente no en archivos de texto.

es probable que desee un bucle más como el siguiente:

while(fgets(cLine, N_CLINE, f)) { 
    /* do something */ ; 
} 

donde N_CLINE es el número de bytes disponibles en el tampón de partida un cLine.

La función fgets() es una forma muy preferida de leer una línea de un archivo. Su segundo parámetro es el tamaño del búfer, y lee hasta 1 bytes menos que el tamaño del archivo en el búfer. Siempre termina el búfer con un carácter nul para que pueda pasar con seguridad a otras funciones de cadena C.

Se detiene en el primero del final del archivo, nueva línea o buffer_size-1 bytes leídos.

Deja el carácter de nueva línea en el búfer, y ese hecho le permite distinguir una única línea más larga que su búfer de una línea más corta que el búfer.

Devuelve NULL si no se copiaron bytes debido al final del archivo o un error, y el puntero al búfer en caso contrario. Es posible que desee utilizar feof() y/o ferror() para distinguir esos casos.

+0

gracias, lo hice, pero me pregunto qué pasaría si mi línea es mayor que el tamaño que configuro cortará parte de la siguiente línea o podría causar otros problemas –

+0

Si una línea de entrada es más larga que el búfer pasado a fgets(), dejará de leer antes del final de la línea de entrada y le dará qué leyó hasta ahora en el buffer. Ya sabes que sucedió porque no hay \ n al final del búfer. Cada llamada a fgets() seguirá leyendo, por lo que puede manejar una línea larga un búfer a la vez bucleando hasta que un búfer finalice con \ n. El único problema es analizar sensiblemente tu entrada cuando se rompa en un lugar arbitrario. – RBerteig

1

Si prueba while(fscanf(f, "%27[^\n\r]", cLine) == 1) puede tener un poco más de suerte. Los tres cambios desde el original:

  • longitud límite de lo que se lee en - He usado 27 aquí como un ejemplo, y por desgracia, la familia scanf() requieren la anchura del campo, literalmente, en la cadena de formato y no se puede utilizar el mecanismo * que la lata printf() para pasar el valor de
  • deshacerse de la s en la cadena de formato - %[ es el especificador de formato de "todos los caracteres a juego o que no coincida con un conjunto", y el conjunto se termina por un ] en sí mismo
  • compare el valor de retorno agai NST el número de conversiones que esperamos que suceda (y para facilitar la gestión, garantizar que el número es 1)

Dicho esto, obtendrá el mismo resultado con menos dolor mediante el uso de fgets() de leer la mayor parte posible una línea que cabe en tu memoria intermedia.

+1

Esto todavía lo dejará con su problema original de solo leer la primera línea. Mejor sería "% 27 [^ \ n \ r]% * [\ n \ r]", por lo que el carácter no coincidente se consume. – Dingo

1

Usar fscanf para leer/ensamblar un archivo siempre resulta en código frágil o dolor y sufrimiento. Leer una línea, y tokenizar o escanear esa línea es seguro y efectivo. Necesita más líneas de código, lo que significa que se necesita más tiempo para pensar en lo que quiere hacer (y necesita manejar un tamaño de búfer de entrada finito), pero después de eso, la vida huele menos.

No luche contra fscanf. Simplemente no lo use. Nunca.

2

Si desea leer un archivo línea por línea (Aquí, separador de línea == '\ n') que acaba de hacer:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

int main(int argc, char **argv) 
{ 
     FILE *fp; 
     char *buffer; 
     int ret; 

     // Open a file ("test.txt") 
     if ((fp = fopen("test.txt", "r")) == NULL) { 
       fprintf(stdout, "Error: Can't open file !\n"); 
       return -1; 
     } 
     // Alloc buffer size (Set your max line size) 
     buffer = malloc(sizeof(char) * 4096); 
     while(!feof(fp)) 
     { 
       // Clean buffer 
       memset(buffer, 0, 4096); 
       // Read a line 
       ret = fscanf(fp, "%4095[^\n]\n", buffer); 
       if (ret != EOF) { 
         // Print line 
         fprintf(stdout, "%s\n", buffer); 
       } 
     } 
     // Free buffer 
     free(buffer); 
     // Close file 
     fclose(fp); 
     return 0; 
} 

Enjoy :)

0

Creo que el problema con este código es porque cuando lee con% [^ \ n \ r] s, de hecho, lee hasta llegar a '\ n' o '\ r', pero no lee '\ n' o '\ r' también . Por lo tanto, debe obtener este carácter antes de volver a leer con fscanf en el ciclo. Haga algo como eso:

do{ 
    fscanf(f, "%[^\n\r]s", cLine) != EOF 

    /* Do something here */ 

}while(fgetc(file) != EOF) 
Cuestiones relacionadas