C lee el archivo línea por línea

8 minutos de lectura

avatar de usuario
hierro

Escribí esta función para leer una línea de un archivo:

const char *readLine(FILE *file) {

    if (file == NULL) {
        printf("Error: file pointer is null.");
        exit(1);
    }

    int maximumLineLength = 128;
    char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);

    if (lineBuffer == NULL) {
        printf("Error allocating memory for line buffer.");
        exit(1);
    }

    char ch = getc(file);
    int count = 0;

    while ((ch != '\n') && (ch != EOF)) {
        if (count == maximumLineLength) {
            maximumLineLength += 128;
            lineBuffer = realloc(lineBuffer, maximumLineLength);
            if (lineBuffer == NULL) {
                printf("Error reallocating space for line buffer.");
                exit(1);
            }
        }
        lineBuffer[count] = ch;
        count++;

        ch = getc(file);
    }

    lineBuffer[count] = '\0';
    char line[count + 1];
    strncpy(line, lineBuffer, (count + 1));
    free(lineBuffer);
    const char *constLine = line;
    return constLine;
}

La función lee el archivo correctamente y, al usar printf, veo que la cadena constLine también se leyó correctamente.

Sin embargo, si uso la función, por ejemplo, así:

while (!feof(myFile)) {
    const char *line = readLine(myFile);
    printf("%s\n", line);
}

printf genera un galimatías. ¿Por qué?

  • Usar fgets en vez de fgetc. Estás leyendo carácter por carácter en lugar de línea por línea.

    – Puñal

    17 de marzo de 2017 a las 4:00

  • Tenga en cuenta que getline() es parte de POSIX 2008. Puede haber plataformas similares a POSIX sin él, especialmente si no son compatibles con el resto de POSIX 2008, pero dentro del mundo de los sistemas POSIX, getline() es bastante portátil en estos días.

    –Jonathan Leffler

    8 mayo 2017 a las 21:20

avatar de usuario
mbaitoff

Si su tarea no es inventar la función de lectura línea por línea, sino simplemente leer el archivo línea por línea, puede usar un fragmento de código típico que involucre el getline() función (ver la página del manual aquí):

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    fp = fopen("/etc/motd", "r");
    if (fp == NULL)
        exit(EXIT_FAILURE);

    while ((read = getline(&line, &len, fp)) != -1) {
        printf("Retrieved line of length %zu:\n", read);
        printf("%s", line);
    }

    fclose(fp);
    if (line)
        free(line);
    exit(EXIT_SUCCESS);
}

  • Más precisamente, este getline es específico de GNU libc, es decir, de Linux. Sin embargo, si la intención es tener una función de lectura de líneas (en lugar de aprender C), hay varias funciones de lectura de líneas de dominio público disponibles en la web.

    – Gilles ‘SO- deja de ser malvado’

    17 de agosto de 2010 a las 11:55

  • ¿Por qué debería hacer eso? Lea el manual, el búfer se reasigna en cada llamada, luego debe liberarse al final.

    – mbaitoff

    30 de noviembre de 2012 a las 12:43

  • Él if(line) el cheque es superfluo. Vocación free(NULL) es esencialmente un no-op.

    – aroth

    28 de enero de 2014 a las 7:25

  • @PhilipAdler Si realmente quieres pelear free(NULL) siendo no especificado (aunque estoy bastante seguro de que en ninguna parte está escrito así), entonces debes saber que incluso ls llamadas free(NULL). Después de verificar, la página del manual dice que free(ptr); free(ptr); es indefinido, y eso free(NULL) no hace nada. @mbaitoff Entonces, ¿por qué te molestas en liberar line entonces ? Aún así, este sitio web tiene que ver con enseñar o ayudar con el mejor solución posible, y liberar cada memoria asignada que ya no se usa es en realidad una buena práctica.

    – Jerska

    13 de junio de 2014 a las 18:59


  • Para aquellos que dijeron que este getline es específico de GNU libc, “Tanto getline() como getdelim() eran originalmente extensiones de GNU. Se estandarizaron en POSIX.1-2008”.

    – willkill07

    21/04/2015 a las 21:01

avatar de usuario
Robar

FILE* filePointer;
int bufferLength = 255;
char buffer[bufferLength]; /* not ISO 90 compatible */

filePointer = fopen("file.txt", "r");

while(fgets(buffer, bufferLength, filePointer)) {
    printf("%s\n", buffer);
}

fclose(filePointer);

  • Para mí, esto da como resultado sobrescribir cada línea con la siguiente. Vea esta pregunta basada en la respuesta anterior.

    – Cezar Cobuz

    8 de enero de 2019 a las 3:09


  • porque el elenco (FILE*) fp ? no es fp ya es un FILE * y también fopen() devuelve un FILE * ?

    – Contador م

    04/04/2019 a las 23:25


  • Si está de acuerdo con que las líneas se limiten a una cierta longitud, esta es la mejor respuesta. De lo contrario usando getline es una buena alternativa. estoy de acuerdo con FILE * el elenco es innecesario.

    – theicfire

    17 de octubre de 2019 a las 2:18


  • Eliminé el molde innecesario, agregué una variable para la longitud del búfer y cambié fp para filePointer para mayor claridad.

    – robo

    6 de enero de 2020 a las 11:13

  • Debería ser const int bufferLength si es pedante 🙂

    – baz

    12 de noviembre de 2021 a las 20:34


avatar de usuario
gsamaras

Una completa, fgets() solución:

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

#define MAX_LEN 256

int main(void)
{
    FILE* fp;
    fp = fopen("file.txt", "r");
    if (fp == NULL) {
      perror("Failed: ");
      return 1;
    }

    char buffer[MAX_LEN];
    while (fgets(buffer, MAX_LEN, fp))
    {
        // Remove trailing newline
        buffer[strcspn(buffer, "\n")] = 0;
        printf("%s\n", buffer);
    }

    fclose(fp);
    return 0;
}

Producción:

First line of file
Second line of file
Third (and also last) line of file

Recuerde, si desea leer desde la entrada estándar (en lugar de un archivo como en este caso), todo lo que tiene que hacer es pasar stdin como el tercer parámetro de fgets() método, así:

while(fgets(buffer, MAX_LEN, stdin))

Apéndice

Eliminar el carácter de nueva línea final de la entrada fgets ()

como detectar un archivo esta abierto o no en c

  • Hola @gsamaras creo que directamente podemos pasar MAX_LEN a fgets. Encontré esta pieza de descripción en: linux.die.net/man/3/fgets “` “`

    usuario10277898

    17 de noviembre de 2020 a las 10:23


  • Oye @juancortez, me paso MAX_LEN - 1 en el segundo argumento del método de hecho!

    – gsamaras

    17 de noviembre de 2020 a las 19:36

  • No hay necesidad de la -1 en MAX_LEN - 1fgets(buffer, n, fp) ya lee hasta n-1 caracteres reservando espacio para la terminación nula.

    – マルちゃん だよ

    26 de noviembre de 2021 a las 13:46

  • @マルちゃん だよ Sí, tienes razón cplusplus.com/reference/cstdio/fgetsrespuesta actualizada.

    – gsamaras

    28 de noviembre de 2021 a las 8:49

avatar de usuario
RevoLab

//open and get the file handle
FILE* fh;
fopen_s(&fh, filename, "r");

//check if file exists
if (fh == NULL){
    printf("file does not exists %s", filename);
    return 0;
}


//read line by line
const size_t line_size = 300;
char* line = malloc(line_size);
while (fgets(line, line_size, fh) != NULL)  {
    printf(line);
}
free(line);    // dont forget to free heap memory

avatar de usuario
Gilles ‘SO- deja de ser malvado’

En tus readLine función, devuelve un puntero a la line matriz (estrictamente hablando, un puntero a su primer carácter, pero la diferencia es irrelevante aquí). Dado que es una variable automática (es decir, está “en la pila”), la memoria se recupera cuando la función regresa. Ves galimatías porque printf ha puesto sus propias cosas en la pila.

Debe devolver un búfer asignado dinámicamente desde la función. Ya tienes uno, es lineBuffer; todo lo que tiene que hacer es truncarlo a la longitud deseada.

    lineBuffer[count] = '\0';
    realloc(lineBuffer, count + 1);
    return lineBuffer;
}

AGREGADO (respuesta a la pregunta de seguimiento en el comentario): readLine devuelve un puntero a los caracteres que componen la línea. Este puntero es lo que necesita para trabajar con el contenido de la línea. También es lo que debes pasar a free cuando hayas terminado de usar la memoria tomada por estos personajes. Así es como puede usar el readLine función:

char *line = readLine(file);
printf("LOG: read a line: %s\n", line);
if (strchr(line, 'a')) { puts("The line contains an a"); }
/* etc. */
free(line);
/* After this point, the memory allocated for the line has been reclaimed.
   You can't use the value of `line` again (though you can assign a new value
   to the `line` variable if you want). */

  • @Iron: He agregado algo a mi respuesta, pero no estoy seguro de cuál es su dificultad, por lo que puede estar fuera de lugar.

    – Gilles ‘SO- deja de ser malvado’

    17 de agosto de 2010 a las 11:53

  • @Iron: la respuesta es que no lo liberas. Usted documenta (en la documentación de la API) el hecho de que el búfer devuelto está malloc’d y la persona que llama debe liberarlo. Luego, las personas que usan su función readLine (¡con suerte!) escribirán un código similar al fragmento que Gilles ha agregado a su respuesta.

    – JeremyP

    17 de agosto de 2010 a las 12:27

avatar de usuario
Akhil V Suku

Aquí están mis varias horas… Leyendo todo el archivo línea por línea.

char * readline(FILE *fp, char *buffer)
{
    int ch;
    int i = 0;
    size_t buff_len = 0;

    buffer = malloc(buff_len + 1);
    if (!buffer) return NULL;  // Out of memory

    while ((ch = fgetc(fp)) != '\n' && ch != EOF)
    {
        buff_len++;
        void *tmp = realloc(buffer, buff_len + 1);
        if (tmp == NULL)
        {
            free(buffer);
            return NULL; // Out of memory
        }
        buffer = tmp;

        buffer[i] = (char) ch;
        i++;
    }
    buffer[i] = '\0';

    // Detect end
    if (ch == EOF && (i == 0 || ferror(fp)))
    {
        free(buffer);
        return NULL;
    }
    return buffer;
}

void lineByline(FILE * file){
char *s;
while ((s = readline(file, 0)) != NULL)
{
    puts(s);
    free(s);
    printf("\n");
}
}

int main()
{
    char *fileName = "input-1.txt";
    FILE* file = fopen(fileName, "r");
    lineByline(file);
    return 0;
}

  • @Iron: He agregado algo a mi respuesta, pero no estoy seguro de cuál es su dificultad, por lo que puede estar fuera de lugar.

    – Gilles ‘SO- deja de ser malvado’

    17 de agosto de 2010 a las 11:53

  • @Iron: la respuesta es que no lo liberas. Usted documenta (en la documentación de la API) el hecho de que el búfer devuelto está malloc’d y la persona que llama debe liberarlo. Luego, las personas que usan su función readLine (¡con suerte!) escribirán un código similar al fragmento que Gilles ha agregado a su respuesta.

    – JeremyP

    17 de agosto de 2010 a las 12:27

avatar de usuario
Escapar de Raku

Usar fgets() para leer una línea de un identificador de archivo.

¿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