我正在尝试从文本文件中读取所有内容。这是我写的代码。
#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;
}
我想知道这个函数的相对复杂性是多少?
答案 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
或类似。
特别是,如果您只是阅读并且多个进程同时执行此操作,则整个系统的内存中只有一个副本。