2010-03-28 10 views
26

Quiero leer en un archivo línea por línea, sin conocer la longitud de la línea anterior. Esto es lo que tengo hasta ahora:Leer línea desde archivo sin conocer la longitud de la línea

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 averiguar la longitud de la línea, pero sólo para las líneas que son más cortas que 4095 caracteres, además de las dos matrices de char parecer una manera incómoda de hacer la tarea. ¿Hay alguna forma mejor de hacerlo (ya he usado fgets() pero me dijeron que no era la mejor manera)?

--Ry

Respuesta

14

Puede comenzar con un poco de tamaño adecuado de su elección y luego usar realloc 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); 

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

+1

Es 'realloc' not' relloc'. –

+0

Al igual que una nota, la lectura carácter por carácter es extremadamente lenta. Debe leerlo en trozos grandes (4-16k). – Blindy

+4

@Blindy: optimización prematura ... –

1

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

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

Después de que esté listo para la próxima línea, puede copiar el "resto" de la entrada sobre la línea actual (básicamente un desplazamiento a la izquierda) y rellenar el resto del búfer con los datos de la entrada. Luego vuelve otra vez hasta que te quedes 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 de io razonable.

5

Es posible que desee mirar en Chuck B. Falconer's public domain ggets library. Si está en un sistema con glibc, probablemente tenga una función (no estándar) getline disponible para usted.

+0

¡Agradable! Creo que puedo confiar en la mayoría de los sistemas tipo UNIX para tener glibc instalado, por lo que esta es definitivamente una gran manera de leer en líneas. – ryyst

+0

Además, 'getline' se ha incluido en el estándar POSIX más reciente, por lo que * es * estándar en Unix ahora. Sin embargo, aún no hay garantía de que esté incluido con c * per se *. – dmckee

1

Esa es la forma en que lo hice por la entrada estándar, si se llama a ella como readLine(NULL, 0) la función asigna un búfer para usted con el tamaño de 1024 y dejar que crezca en pasos de 1024. Si se llama a la función con readLine(NULL, 10) se obtiene una búfer con pasos de 10. Si tiene un búfer puede suministrarlo 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; 
} 
Cuestiones relacionadas