Чтение всего содержимого из текстового файла - C - PullRequest
4 голосов
/ 01 августа 2010

Я пытаюсь прочитать весь контент из текстового файла.Вот код, который я написал.

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

#define PAGE_SIZE 1024

static char *readcontent(const char *filename)
{
    char *fcontent = NULL, c;
    int index = 0, pagenum = 1;
    FILE *fp;
    fp = fopen(filename, "r");

    if(fp) {
        while((c = getc(fp)) != EOF) {
            if(!fcontent || index == PAGE_SIZE) {
                fcontent = (char*) realloc(fcontent, PAGE_SIZE * pagenum + 1);
                ++pagenum;
            }
            fcontent[index++] = c;
        }
        fcontent[index] = '\0';
        fclose(fp);
    }
    return fcontent;
}

static void freecontent(char *content)
{
    if(content) {
        free(content);
        content = NULL;
    }
}

Это использование

int main(int argc, char **argv)
{
    char *content;
    content = readcontent("filename.txt");
    printf("File content : %s\n", content);
    fflush(stdout);
    freecontent(content);
    return 0;
}

Поскольку я новичок в C, мне интересно, выглядит ли этот код идеально?Видите ли вы какие-либо проблемы / улучшения?

Используемый компилятор: GCC.Но ожидается, что этот код будет кроссплатформенным.

Любая помощь будет оценена.

Редактировать

Вот обновленный код с fread и ftell.

static char *readcontent(const char *filename)
{
    char *fcontent = NULL;
    int fsize = 0;
    FILE *fp;

    fp = fopen(filename, "r");
    if(fp) {
        fseek(fp, 0, SEEK_END);
        fsize = ftell(fp);
        rewind(fp);

        fcontent = (char*) malloc(sizeof(char) * fsize);
        fread(fcontent, 1, fsize, fp);

        fclose(fp);
    }
    return fcontent;
}

Мне интересно, что будет относительнымсложность этой функции?

Ответы [ 6 ]

7 голосов
/ 01 августа 2010

Вам следует попробовать поискать функции fsize ( О fsize, см. Обновление ниже ) и fread. Это может стать огромным улучшением производительности.

Используйте fsize, чтобы получить размер файла, который вы читаете. Используйте этот размер только для одного выделения памяти. ( О fsize, см. Обновление ниже. Идея получить размер файла и выполнить одно выделение остается неизменной ).

Используйте fread, чтобы заблокировать чтение файла. Это намного быстрее, чем чтение файла одним знаком.

Примерно так:

long size = fsize(fp);
fcontent = malloc(size);
fread(fcontent, 1, size, fp);

Обновление

Не уверен, что fsize является кроссплатформенным, но вы можете использовать этот метод, чтобы получить размер файла:

fseek(fp, 0, SEEK_END); 
size = ftell(fp);
fseek(fp, 0, SEEK_SET); 
2 голосов
/ 01 августа 2010

Возможно, он медленнее и сложнее, чем:

while((c = getc(fp)) != EOF) {
    putchar(c);
}

, что делает то же самое, что и ваш код.

2 голосов
/ 01 августа 2010

Люди часто realloc удваивают существующий размер, чтобы получить амортизированное постоянное время вместо линейного. Это делает буфер не более чем в два раза больше, что обычно нормально, и у вас есть возможность перераспределить его обратно до нужного размера после того, как вы закончите.

Но еще лучше - stat(2) для размера файла и выделить его один раз (с некоторым дополнительным пространством, если размер файла изменчив).

Кроме того, почему вы не либо fgets(3) вместо чтения символа за символом, либо, что еще лучше, mmap(2) всего (или соответствующего блока, если он слишком велик для памяти).

1 голос
/ 12 августа 2011

Одна проблема, которую я вижу здесь, это переменная index, которая не уменьшается.Таким образом, условие if(!fcontent || index == PAGE_SIZE) будет выполнено только один раз.Поэтому я думаю, что проверка должна быть как index%PAGE_SIZE == 0 вместо index == PAGE_SIZE.

1 голос
/ 01 августа 2010

Это из краткого прочтения, поэтому я мог пропустить несколько вопросов.

Во-первых, a = realloc(a, ...); не так.В случае сбоя realloc() возвращается NULL, но исходная память не освобождается.Так как вы переназначаете a, оригинальная память теряется (т.е. это утечка памяти).Правильный способ сделать это: tmp = realloc(a, ...); if (tmp) a = tmp; и т. Д.

Во-вторых, при определении размера файла с помощью fseek(fp, 0, SEEK_END); обратите внимание, что это может работать, а может и не работать.Если файл не имеет произвольного доступа (например, stdin), вы не сможете вернуться к началу, чтобы прочитать его.Кроме того, fseek(), за которым следует ftell(), могут не дать значимого результата для двоичных файлов.А для текстовых файлов может не указываться нужное количество символов, которые можно прочитать.По этой теме есть некоторая полезная информация по comp.lang.c FAQ вопрос 19.2 .

Кроме того, в исходном коде вы не устанавливаете index в 0, когда оно равно PAGESIZE, поэтому, если длина вашего файла превышает 2*PAGESIZE, вы перезапишите буфер.

Ваша функция freecontent():

static void freecontent(char *content)
{
    if(content) {
        free(content);
        content = NULL;
    }
}

бесполезна.Он устанавливает только копию от content до NULL.Это похоже на то, как если бы вы написали функцию setzero, например:

void setzero(int i) { i = 0; }

Гораздо лучшая идея - следить за памятью самостоятельно, а не освобождать что-либо более или менее необходимое.

Не следует приводить возвращаемое значение malloc() или realloc() в C, поскольку void * неявно преобразуется в любой другой тип указателя объекта в C.

Надеюсь, что это поможет.

0 голосов
/ 01 августа 2010

В системах POSIX (например, linux) вы можете получить тот же эффект с помощью системного вызова mmap, который отображает все ваши файлы в памяти. У него есть возможность отобразить этот файл copy при записи , так что вы измените файл, если измените буфер.

Обычно это будет намного эффективнее, поскольку вы оставляете столько, сколько можете, для системы. Не нужно делать realloc или подобное.

В частности, если вы только читаете, и несколько процессов делают это одновременно, в памяти будет только одна копия для всей системы.

...