7

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

#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;
}

我想知道这个函数的相对复杂性是多少?

4

6 回答 6

11

您应该尝试查看功能fsize关于 fsize,请参阅下面的更新)和fread. 这可能是一个巨大的性能改进。

用于fsize获取您正在阅读的文件的大小。仅使用此大小进行一次内存分配。(关于fsize,见下文更新。获取文件大小,做一次alloc的思路还是一样的)。

用于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); 
于 2010-08-01T06:58:51.993 回答
2

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

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

另外,为什么您不fgets(3)阅读而不是逐个阅读字符,或者甚至更好地阅读mmap(2)整个内容(或相关的块,如果它对于内存来说太大了)。

于 2010-08-01T07:04:45.563 回答
2

它可能比以下内容更慢并且肯定更复杂:

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

它和你的代码做同样的事情。

于 2010-08-01T07:21:13.870 回答
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常见问题解答问题 19.2中有一些关于此主题的有用信息。

此外,在您的原始代码中,当它等于 时,您不会设置index为 0 PAGESIZE,因此如果您的文件长度大于2*PAGESIZE,您将覆盖缓冲区。

你的freecontent()功能:

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

没用。它只设置contentto的副本NULL。就像您编写这样的函数setzero一样:

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

一个更好的主意是自己跟踪内存,而不是释放任何比需要更多或更少的东西。

您不应该在 C 中转换malloc()or的返回值realloc(),因为 a 在 C 中void *被隐式转换为任何其他对象指针类型。

希望有帮助。

于 2010-08-01T07:39:08.613 回答
1

我在这里可以看到的一个问题index是不递减的变量。所以这个条件 if(!fcontent || index == PAGE_SIZE)只会成立一次。所以我认为 check 应该像 index%PAGE_SIZE == 0而不是index == PAGE_SIZE.

于 2011-08-12T06:23:35.920 回答
0

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

这通常会更有效率,因为您尽可能多地留给系统。不需要做realloc或类似的事情。

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

于 2010-08-01T07:37:36.300 回答