从文本文件中读取所有内容 - C.

时间:2010-08-01 06:54:27

标签: c file-io dynamic-memory-allocation

我正在尝试从文本文件中读取所有内容。这是我写的代码。

#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。但是这段代码有望跨平台。

任何帮助将不胜感激。

修改

以下是包含freadftell的更新代码。

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 个答案:

答案 0 :(得分:7)

您应该尝试查看函数fsize关于fsize,请参阅下面的更新)和fread。这可能会带来巨大的性能提升。

使用fsize获取您正在阅读的文件的大小。使用此大小仅执行一次内存分配。 (关于fsize,请参阅下面的更新。获取文件大小和执行一个alloc的想法仍然相同)。

使用fread来阻止读取文件。这比单个charecter读取文件要快得多。

这样的事情:

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); 

答案 1 :(得分:2)

人们经常realloc是现有规模的两倍,以获得摊销的常数时间而非线性。这使缓冲区的大小不超过两倍,这通常是可以的,并且您可以选择在完成后重新分配回正确的大小。

但更好的是文件大小为stat(2)并分配一次(如果文件大小不稳定,则增加一些空间)。

另外,为什么你不是fgets(3)而不是逐个字符地阅读,或者更好的是mmap(2)整个事物(或者相关的块,如果它对于内存来说太大)。< / p>

答案 2 :(得分:2)

它可能比以下更慢,更复杂:

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

与您的代码完全相同。

答案 3 :(得分:1)

这是一个快速阅读,所以我可能错过了一些问题。

首先,a = realloc(a, ...);是错误的。如果realloc()失败,则返回NULL,但不释放原始内存。由于您重新分配给a,原始内存将丢失(即,它是内存泄漏)。正确的方法是:tmp = realloc(a, ...); if (tmp) a = tmp;等。

其次,关于使用fseek(fp, 0, SEEK_END);确定文件大小,请注意这可能有效,也可能无效。如果文件不是随机访问(例如stdin),您将无法返回到开头阅读它。此外,fseek()后跟ftell()可能无法为二进制文件提供有意义的结果。对于文本文件,它可能无法为您提供可读取的正确数量的字符。有关此主题的一些有用信息,请参见comp.lang.c常见问题解答question 19.2

此外,在原始代码中,如果index等于PAGESIZE,则不会将2*PAGESIZE设置为0,因此如果文件长度大于freecontent(),则会覆盖缓冲区

您的static void freecontent(char *content) { if(content) { free(content); content = NULL; } } 功能:

content

没用。它只会将NULL的副本设置为setzero。就像你写了一个函数void setzero(int i) { i = 0; } 一样:

malloc()

更好的想法是自己跟踪记忆,而不是任何比所需更多或更少的东西。

您不应在C中转换realloc()void *的返回值,因为{{1}}会隐式转换为C中的任何其他对象指针类型。

希望有所帮助。

答案 4 :(得分:1)

我在这里可以看到的一个问题是变量index,它是非减少的。所以条件 if(!fcontent || index == PAGE_SIZE)只会是一次。所以我认为检查应该是这样的 index%PAGE_SIZE == 0代替index == PAGE_SIZE

答案 5 :(得分:0)

在POSIX系统(例如linux)上,您可以通过将所有文件映射到内存中的系统调用mmap获得相同的效果。它可以选择在写入上映射该文件副本,因此如果更改缓冲区,则会覆盖文件。

这通常会更有效率,因为您尽可能多地离开系统。无需realloc或类似。

特别是,如果您只是阅读并且多个进程同时执行此操作,则整个系统的内存中只有一个副本。