Чтение текстового файла в массив строк в C - PullRequest
5 голосов
/ 26 ноября 2009

Используя C, я хотел бы прочитать содержимое текстового файла таким образом, чтобы, когда все сказано, и был выполнен массив строк с n-й строкой, представляющей n-ю строку текстового файла. Строки файла могут быть произвольно длинными.

Какой элегантный способ сделать это? Мне известны некоторые хитрые приемы чтения текстового файла непосредственно в один буфер соответствующего размера, но разбиение его на строки делает его более хитрым (по крайней мере, насколько я могу судить).

Большое спасибо!

Ответы [ 6 ]

6 голосов
/ 26 ноября 2009

Разбиение его на строки означает анализ текста и замену всех символов EOL (под EOL, я имею в виду \ n и \ r) символами 0. Таким образом, вы можете фактически повторно использовать свой буфер и сохранять только начало каждой строки в отдельном массиве char * (все, выполнив всего 2 прохода).

Таким образом, вы можете выполнить одно чтение для всего размера файла + 2 анализа, что, вероятно, повысит производительность.

1 голос
/ 29 марта 2010

Можно прочитать количество строк в файле (цикл fgets), а затем создать двумерный массив с первым измерением, равным количеству строк + 1. Затем просто перечитайте файл в массив.

Вам нужно будет определить длину элементов. Или подсчитайте длину самой длинной строки.

Пример кода:

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 голосов
/ 13 сентября 2012

вы можете использовать этот способ

#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;
}
0 голосов
/ 26 ноября 2009

Если у вас есть хороший способ прочитать весь файл в память, вы почти у цели. После того как вы сделали это, вы можете отсканировать файл дважды. Один раз для подсчета строк и один раз для установки указателей строк и замены '\ n' и (и, возможно, '\ r', если файл читается в двоичном режиме Windows) на '\ 0'. В промежутках между сканированиями выделяется массив указателей, теперь вы знаете, сколько вам нужно.

0 голосов
/ 26 ноября 2009

Возможно, лучший способ сделать это - связанный список? Компилятору не понравится наличие массива без представления о том, насколько большим он будет. С помощью связанного списка вы можете иметь действительно большой текстовый файл, и вам не нужно беспокоиться о выделении достаточного количества памяти для массива.

К сожалению, я не научился создавать связанные списки, но, возможно, кто-то еще может вам помочь.

0 голосов
/ 26 ноября 2009

Для C (в отличие от C ++) вам, вероятно, понадобится использовать fgets(). Однако вы можете столкнуться с проблемами из-за линий произвольной длины.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...