Problemas para leer una línea usando fscanf()

8 minutos de lectura

Estoy tratando de 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 manera de leer una línea? ¿Qué debo arreglar para que funcione como se espera?

avatar de usuario
paxdiablo

Casi siempre una mala idea usar el fscanf() ya que puede dejar el puntero de su archivo en una ubicación desconocida en caso de falla.

prefiero usar fgets() para obtener cada línea y luego sscanf() que. Luego puede continuar examinando la línea leída 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á tratando de hacer, leyendo una cadena hasta que encuentre un carácter de nueva línea.

  • ¿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!

    sofsr

    14 de mayo de 2009 a las 7:02

  • 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

    14 de mayo de 2009 a las 7:11

  • stackoverflow.com/questions/865335/…

    – Rob Kam

    15 de mayo de 2009 a las 6:04

Si desea 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;
}

Disfrutar 🙂

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

  • límite de longitud de lo que se lee – he usado 27 aquí como un ejemplo, y desafortunadamente el scanf() La familia requiere el ancho del campo literalmente en la cadena de formato y no puede usar el * mecanismo que el printf() can para pasar el valor en
  • deshacerse de s en la cadena de formato – %[ is the format specifier for “all characters matching or not matching a set”, and the set is terminated by a ] por sí mismo
  • compare el valor de retorno con el número de conversiones que espera que sucedan (y para facilitar la administración, asegúrese de que el número sea 1)

Dicho esto, obtendrá el mismo resultado con menos dolor usando fgets() para leer tanto de una línea como quepa en su búfer.

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

    – Dingo

    15 de mayo de 2009 a las 2:08

El uso de fscanf para leer/tokenizar un archivo siempre da como resultado un 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 lleva más tiempo 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 apesta menos.

No luches contra fscanf. Simplemente no lo uses. Siempre.

avatar de usuario
JSB

Me parece que está tratando de usar operadores de expresiones regulares en su cadena fscanf. La cuerda [^\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 sucede en su caso es que la primera llamada a fscanf() lee hasta el final del archivo en busca de una cadena coincidente, luego devuelve 0 para informarle 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 hasta 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/

  • [^a-z] en realidad excluye az en scanf. Aunque, como se señaló anteriormente, la cadena busca “un par de caracteres, el primero que no es un salto de línea y el segundo es una s”

    – Tordek

    14 de mayo de 2009 a las 7:16

  • La documentación de fscanf en cplusplus.com está incompleta. Busque en Google ‘fscanf scanset’.

    – Dingo

    15 de mayo de 2009 a las 1:46

avatar de usuario
rberteig

Su bucle tiene varios problemas. Tu escribiste:

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 un retorno válido de cero, en cuyo caso no hay contenido nuevo en el búfer cLine de una lectura exitosa.

  2. Tiene un problema cuando se produce una falla en la coincidencia porque es difícil predecir hacia dónde apunta ahora el identificador de archivo en la secuencia. Esto hace que la recuperación de una coincidencia fallida sea más difícil de lo esperado.

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

  4. No ha protegido su búfer de un desbordamiento. Se puede leer cualquier número 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 la elección de los atacantes.

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

Probablemente quieras 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 búfer que inicia un cLine.

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

Se detiene en el primero de fin de 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 sola 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.

  • [^a-z] en realidad excluye az en scanf. Aunque, como se señaló anteriormente, la cadena busca “un par de caracteres, el primero que no es un salto de línea y el segundo es una s”

    – Tordek

    14 de mayo de 2009 a las 7:16

  • La documentación de fscanf en cplusplus.com está incompleta. Busque en Google ‘fscanf scanset’.

    – Dingo

    15 de mayo de 2009 a las 1:46

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

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

    /* Do something here */

}while(fgetc(file) != EOF)

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad