txt - Problemas para leer una línea usando fscanf()
leer un archivo en c++ (7)
Intento leer una línea usando el siguiente código:
while(fscanf(f, "%[^/n/r]s", cLine) != EOF )
{
/* do something with cLine */
}
Pero de alguna manera solo obtengo la primera línea cada vez. ¿Es esta una mala forma de leer una línea? ¿Qué debería arreglar para que funcione como se esperaba?
Casi siempre es una mala idea usar la fscanf()
ya que puede dejar su puntero de archivo en una ubicación desconocida cuando falla.
Prefiero usar fgets()
para obtener cada línea y luego sscanf()
eso. Luego puede continuar examinando la lectura de línea como mejor le parezca. Algo 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, leyendo en una cadena hasta que encuentres un carácter de nueva línea.
Me parece que estás tratando de usar operadores de expresiones regulares en tu cadena 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 () lee todo el camino 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 de fscanf para obtener más información: http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/
Si prueba while( fscanf( f, "%27[^/n/r]", cLine ) == 1 )
puede tener un poco más de suerte. Los tres cambios de tu original:
- límite de longitud lo que se lee - He usado
27
aquí como ejemplo, y desafortunadamente la familiascanf()
requiere el ancho de campo literalmente en la cadena de formato y no puede usar el*
mecanismo que elprintf()
puede pasar el valor en - deshacerse de la
s
en el formato de cadena -%[
es el especificador de formato para "todos los caracteres que coinciden o no coinciden con un conjunto", y el conjunto se termina por un]
por sí mismo - compare el valor de retorno con el número de conversiones que espera que suceda (y para facilitar la administración, asegúrese de que el número sea 1)
Dicho esto, obtendrás el mismo resultado con menos dolor al usar fgets()
para leer en la mayor cantidad posible de una línea en tu memoria intermedia.
Su ciclo tiene varios problemas. Tu escribiste:
while( fscanf( f, "%[^/n/r]s", cLine ) != EOF )
/* do something */;
Algunas cosas a considerar:
fscanf () devuelve la cantidad de elementos almacenados. Puede devolver EOF si lee más allá del final del archivo o si el identificador del archivo tiene un error.
cLine
distinguir una devolución válida de cero, en cuyo caso no hay contenido nuevo en la líneacLine
del buffer desde una lectura exitosa.Tiene un problema cuando falla la coincidencia porque es difícil predecir dónde apunta el archivo ahora 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.
El patrón que escribiste probablemente no hace lo que pretendías. Está haciendo coincidir cualquier número de caracteres que no son CR o LF, y luego espera encontrar un literal
s
.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 lamentablemente común, que en muchos casos puede ser explotado por un atacante para ejecutar código arbitrario de los atacantes que elijan.
A menos que solicite 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 parecido al siguiente:
while(fgets(cLine, N_CLINE, f)) {
/* do something */ ;
}
donde N_CLINE es la cantidad de bytes disponibles en el buffer que inicia una línea cLine
.
La 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
lectura de bytes.
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 buffer de lo contrario. Es posible que desee usar feof()
y / o ferror()
para distinguir esos casos.
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.
Si quiere leer un archivo línea por línea (Aquí, separador de línea == ''/ n'') simplemente haga eso:
#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;
}
Disfruta :)
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 el ''/ n ''o'' / r ''también. Así que debes obtener este personaje antes de leer con fscanf nuevamente en loop. Haz algo como eso:
do{
fscanf(f, "%[^/n/r]s", cLine) != EOF
/* Do something here */
}while(fgetc(file) != EOF)