2009-11-25 20 views
5

Al usar CI, me gustaría leer el contenido de un archivo de texto de modo que cuando todo esté dicho y hecho una serie de cadenas con la enésima cadena que representa el enésimo línea del archivo de texto. Las líneas del archivo pueden ser arbitrariamente largas.Lectura de archivos de texto en una matriz de líneas en C

¿Qué es una forma elegante de lograr esto? Sé de algunos buenos trucos para leer un archivo de texto directamente en un único búfer de tamaño adecuado, pero dividirlo en líneas lo hace más complicado (al menos hasta donde sé).

Muchas gracias!

Respuesta

6

Descomponerlo en líneas significa analizar el texto y reemplazar todos los caracteres EOL (por EOL me refiero \ n y \ r) con 0. De esta manera, puede reutilizar su memoria intermedia y almacenar solo el comienzo de cada línea en una matriz de caracteres * separada (todo haciendo solo 2 pases).

De esta manera podría hacer una lectura para todo el tamaño de archivo + 2 parses que probablemente mejoraría el rendimiento.

+0

Esta es sin duda la mejor manera, aunque podría requerir más de un pase todo el archivo. Necesita contar las líneas (para que pueda asignar la matriz de tamaño correcta), reemplace \ n con 0 y luego asigne el inicio de cada línea al lugar correcto en la matriz. Por supuesto, puedes hacer esto en dos pases. –

+0

Una muy buena idea. Voy a darle un giro. –

+0

+1 Sin contar la copia inicial del archivo al búfer, puede hacer una sola pasada con 'realloc()' y 'strtok()'. – pmg

0

Para C (en comparación con C++), probablemente termines usando fgets(). Sin embargo, es posible que tenga problemas debido a sus líneas de longitud arbitrarias.

0

¿Quizás una lista vinculada sería la mejor manera de hacerlo? Al compilador no le gustará tener una matriz sin tener idea de su tamaño. Con una lista vinculada, puede tener un archivo de texto realmente grande y no preocuparse por asignar suficiente memoria a la matriz.

Lamentablemente, no he aprendido a hacer listas enlazadas, pero tal vez alguien más pueda ayudarlo.

+0

El tamaño arbitrario es una característica atractiva de las listas vinculadas, pero para obtenerlo se intercambia el acceso aleatorio. Por ejemplo, no puede obtener la línea número 5 sin obtener primero las líneas 0-4. Pero construir una lista vinculada como una estructura intermedia es una buena idea, entonces podrías construir la matriz fácilmente. –

+0

Lamentablemente, una lista vinculada no es muy apropiada en este caso debido a algunos detalles que dejé fuera de la cuestión (en resumen, necesito acceso aleatorio). Pude, por supuesto, leer todo en una lista vinculada, luego copiar el contenido en una matriz, pero esperaba un enfoque más elegante. –

0

Si tiene una buena manera de leer todo el archivo en la memoria, ya casi está allí. Después de que hayas hecho eso, puedes escanear el archivo dos veces. Una vez para contar las líneas, y una vez para establecer los punteros de línea y reemplazar '\ n' y (y tal vez '\ r' si el archivo se lee en modo binario de Windows) con '\ 0'. Entre exploraciones, asigne una matriz de punteros, ahora que sabe cuántos necesita.

1

Es posible leer el número de líneas en el archivo (loop fgets), luego crear una matriz bidimensional con la primera dimensión siendo el número de líneas + 1. Luego, simplemente vuelva a leer el archivo en la matriz.

Sin embargo, tendrá que definir la longitud de los elementos. O bien, cuente el tamaño de línea más largo.

código Ejemplo:

inFile = fopen(FILENAME, "r"); 
lineCount = 0; 
while(inputError != EOF) { 
    inputError = fscanf(inFile, "%s\n", word); 
    lineCount++; 
} 
fclose(inFile); 
    // Above iterates lineCount++ after the EOF to allow for an array 
    // that matches the line numbers 

char names[lineCount][MAX_LINE]; 

fopen(FILENAME, "r"); 
for(i = 1; i < lineCount; i++) 
    fscanf(inFile, "%s", names[i]); 
fclose(inFile); 
0

se puede utilizar de esta manera

#include <stdlib.h> /* exit, malloc, realloc, free */ 
#include <stdio.h> /* fopen, fgetc, fputs, fwrite */ 

struct line_reader { 
    /* All members are private. */ 
    FILE *f; 
    char *buf; 
    size_t siz; 
}; 

/* 
* Initializes a line reader _lr_ for the stream _f_. 
*/ 
void 
lr_init(struct line_reader *lr, FILE *f) 
{ 
    lr->f = f; 
    lr->buf = NULL; 
    lr->siz = 0; 
} 

/* 
* Reads the next line. If successful, returns a pointer to the line, 
* and sets *len to the number of characters, at least 1. The result is 
* _not_ a C string; it has no terminating '\0'. The returned pointer 
* remains valid until the next call to next_line() or lr_free() with 
* the same _lr_. 
* 
* next_line() returns NULL at end of file, or if there is an error (on 
* the stream, or with memory allocation). 
*/ 
char * 
next_line(struct line_reader *lr, size_t *len) 
{ 
    size_t newsiz; 
    int c; 
    char *newbuf; 

    *len = 0;   /* Start with empty line. */ 
    for (;;) { 
     c = fgetc(lr->f); /* Read next character. */ 
     if (ferror(lr->f)) 
      return NULL; 

     if (c == EOF) { 
      /* 
      * End of file is also end of last line, 
     ` * unless this last line would be empty. 
      */ 
      if (*len == 0) 
       return NULL; 
      else 
       return lr->buf; 
     } else { 
      /* Append c to the buffer. */ 
      if (*len == lr->siz) { 
       /* Need a bigger buffer! */ 
       newsiz = lr->siz + 4096; 
       newbuf = realloc(lr->buf, newsiz); 
       if (newbuf == NULL) 
        return NULL; 
       lr->buf = newbuf; 
       lr->siz = newsiz; 
      } 
      lr->buf[(*len)++] = c; 

      /* '\n' is end of line. */ 
      if (c == '\n') 
       return lr->buf; 
     } 
    } 
} 

/* 
* Frees internal memory used by _lr_. 
*/ 
void 
lr_free(struct line_reader *lr) 
{ 
    free(lr->buf); 
    lr->buf = NULL; 
    lr->siz = 0; 
} 

/* 
* Read a file line by line. 
* http://rosettacode.org/wiki/Read_a_file_line_by_line 
*/ 
int 
main() 
{ 
    struct line_reader lr; 
    FILE *f; 
    size_t len; 
    char *line; 

    f = fopen("foobar.txt", "r"); 
    if (f == NULL) { 
     perror("foobar.txt"); 
     exit(1); 
    } 

    /* 
    * This loop reads each line. 
    * Remember that line is not a C string. 
    * There is no terminating '\0'. 
    */ 
    lr_init(&lr, f); 
    while (line = next_line(&lr, &len)) { 
     /* 
     * Do something with line. 
     */ 
     fputs("LINE: ", stdout); 
     fwrite(line, len, 1, stdout); 
    } 
    if (!feof(f)) { 
     perror("next_line"); 
     exit(1); 
    } 
    lr_free(&lr); 

    return 0; 
}