Lea la línea del archivo sin conocer la longitud de la línea

5 minutos de lectura

avatar de usuario
ryyst

Quiero leer en un archivo línea por línea, sin saber la longitud de la línea antes. Esto es lo que tengo hasta ahora:

int ch = getc(file);
int length = 0;
char buffer[4095];

while (ch != '\n' && ch != EOF) {
    ch = getc(file);
    buffer[length] = ch;
    length++;
}

printf("Line length: %d characters.", length);

char newbuffer[length + 1];

for (int i = 0; i < length; i++)
    newbuffer[i] = buffer[i];

newbuffer[length] = '\0';    // newbuffer now contains the line.

Ahora puedo calcular la longitud de la línea, pero solo para líneas que tienen menos de 4095 caracteres, además las dos matrices de caracteres parecen una forma incómoda de hacer la tarea. ¿Hay una mejor manera de hacer esto (ya usé fgets() pero me dijeron que no era la mejor manera)?

–Ry

avatar de usuario
coadicto

Puede comenzar con un tamaño adecuado de su elección y luego usar realloc a mitad de camino si necesita más espacio como:

int CUR_MAX = 4095;
char *buffer = (char*) malloc(sizeof(char) * CUR_MAX); // allocate buffer.
int length = 0;

while ( (ch != '\n') && (ch != EOF) ) {
    if(length ==CUR_MAX) { // time to expand ?
      CUR_MAX *= 2; // expand to double the current size of anything similar.
      buffer = realloc(buffer, CUR_MAX); // re allocate memory.
    }
    ch = getc(file); // read from stream.
    buffer[length] = ch; // stuff in buffer.
    length++;
}
.
.
free(buffer);

Tendrá que comprobar si hay errores de asignación después de las llamadas a malloc y realloc.

  • Solo como nota, la lectura de carácter por carácter es extremadamente lenta. Deberías leerlo en grandes partes (4-16k).

    – Ciego

    28 de marzo de 2010 a las 9:45

  • @Blindy: la E / S de la biblioteca estándar realiza el almacenamiento en búfer, por lo que esto no es (mucho) más lento que leer en fragmentos.

    – JaakkoK

    28 de marzo de 2010 a las 10:12

  • ¿Restablecer el conteo a 0 no causa el desbordamiento del búfer?

    – fbstj

    1 de febrero de 2013 a las 8:38

  • Y como siempre, por favor no emita el resultado de malloc().

    – Quintín

    10 de marzo de 2015 a las 12:57


  • ¿Por qué restablecer el conteo a 0 después de aumentar el tamaño de la memoria? ¿Sigue ahí el recuerdo anterior?

    – Roy Li

    01/04/2015 a las 14:45

Es posible que desee investigar Dominio público de Chuck B. Falconer ggets Biblioteca. Si está en un sistema con glibc, probablemente tenga un (no estándar) getline función disponible para usted.

  • ¡Agradable! Creo que puedo confiar en que la mayoría de los sistemas tipo UNIX tienen instalado glibc, por lo que esta es definitivamente una excelente manera de leer en líneas.

    – ryyst

    28 de marzo de 2010 a las 10:36

  • Es más, getline se ha incluido en el estándar POSIX más reciente, por lo que es estándar en Unix ahora. Todavía no hay garantía de que esté incluido con c per sesin embargo.

    – dmckee — gatito ex-moderador

    9 de junio de 2010 a las 17:32

Usted está cerca. Básicamente, desea leer fragmentos de datos y comprobarlos en busca de \n caracteres. Si encuentras uno, bien, tienes un final de línea. Si no lo hace, debe aumentar su búfer (es decir, asignar un nuevo búfer del doble del tamaño del primero y copiar los datos del primero en el nuevo, luego eliminar el antiguo búfer y cambiar el nombre de su nuevo búfer como el viejo – o simplemente realloc si estás en C), lee un poco más hasta que encuentres un final.

Una vez que tenga su final, el texto desde el principio del búfer hasta el \n el carácter es tu línea. Cópielo en un búfer o trabaje en él en su lugar, depende de usted.

Una vez que esté listo para la siguiente línea, puede copiar el “resto” de la entrada sobre la línea actual (básicamente un desplazamiento a la izquierda) y llenar el resto del búfer con datos de la entrada. Luego vas de nuevo hasta que te quedas sin datos.

Esto, por supuesto, se puede optimizar, con un búfer circular, por ejemplo, pero esto debería ser más que suficiente para cualquier algoritmo enlazado a io razonable.

Así es como lo hice para stdin, si lo llamas así readLine(NULL, 0) la función le asigna un búfer con un tamaño de 1024 y lo deja crecer en pasos de 1024. Si llama a la función con readLine(NULL, 10) obtiene un búfer con pasos de 10. Si tiene un búfer, puede proporcionarlo con su tamaño.

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

char *readLine(char **line, size_t *length)
{
    assert(line != NULL);
    assert(length != NULL);

    size_t count = 0;

    *length = *length > 0 ? *length : 1024;

    if (!*line)
    {
        *line = calloc(*length, sizeof(**line));
        if (!*line)
        {
            return NULL;
        }
    }
    else
    {
        memset(*line, 0, *length);
    }

    for (int ch = getc(stdin); ch != '\n' && ch != EOF; ch = getc(stdin))
    {
        if (count == *length)
        {
            *length += 2;
            *line = realloc(*line, *length);
            if (!*line)
            {
                return NULL;
            }
        }

        (*line)[count] = (char)ch;

        ++count;
    }

    return *line;
}

avatar de usuario
guión-o

Considere el modificador de conversión de formato scanf ‘%m’ (POSIX)

char *arr = NULL ;
    // Read unlimited string, terminated with newline. Similar to dynamic size fgets.
if ( fscanf(stdin, "%m[^\n]", &arr) == 1 ) {
   // Do something with arr
   free(arr) ;
} ;

Citando de la página man de scanf:

Un carácter ‘m’ opcional. Esto se usa con conversiones de cadenas (%s, %c, %[), and relieves the caller of the
need to allocate a corresponding buffer to hold the input: instead, scanf() allocates a buffer of sufficient
size, and assigns the address of this buffer to the corresponding pointer argument, which should be a pointer to
a char * variable (this variable does not need to be initialized before the call). The caller should subsequently free(3) this buffer when it is no longer required

¿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