要求:使用 C 语言将文本文件的每一行读入为数组的一个元素,返回一个 char ** 指针。
由于行长度和文本文件行数均未知,相当于二维 char 数组的两维长度都未定义。由于 getline 函数可以自动扩充 char 数组长度,我最初的想法是使用 getline 得到每行,然后每次对 char ** 进行 realloc,直到读完整个文件。
但是这种做法并不好,首先 getline 是 glibc 的扩展,而不是 C 语言的标准函数,使用除 gcc 以外的编译器是不一定能编译通过的;其次,每次对 char ** 指针进行 realloc 显得代码很 ugly。可以使用 fgets 替代 getline,但是就要自己来控制一维 char 数组的长度。
后来想想,换了一种思路,首先将整个文件读入内存,然后根据 '\n' 的个数来计算文件的行数,作为二维数组的长度,然后将所有的 '\n' 替换成 '\0',并将每一行的指针赋给二维 char 数组,代码如下:
char ** text_2_array(const char *filename)
{
char *p, **array;
int lines;
if(filename == NULL) return NULL;FILE *fp = fopen(filename, "r");
if(fp == NULL) return NULL;/* Get file size. */
fseek(fp, 0L, SEEK_END);
long int f_size = ftell(fp);
fseek(fp, 0L, SEEK_SET);/* Allocate space for file content. */
char *buf = (char *) calloc(f_size, sizeof(char));
if(buf == NULL) return NULL;fread(buf, sizeof(char), f_size, fp);
fclose(fp);/* Get number of lines. */
for(p=strchr(buf, '\n'), lines=1; p!=NULL; p=strchr(p, '\n'), lines++) {
if(*p == '\n') p++;
}/* Allocate space for array; split file buffer to lines by change '\n' to
'\0'. */
array = (char **) calloc(lines+1, sizeof(char*));
array[0] = buf;
for(p=strchr(buf, '\n'), lines=1; p!=NULL; p=strchr(p, '\n')) {
if(*p == '\n') *p++ = '\0';
if(p != NULL) array[lines++] = p;
}
/* Add a terminate NULL pointer. */
array[lines] = NULL;
return array;
}
其实读文本文件入数组这个功能在很多语言中是很简单的操作,比如 PHP 的 file 函数,或者 Bash 的 (`cat filename`),都可以直接实现这个功能。但是对 C 这种更低级的语言来说,貌似就没那么简单了。我想要了解的是,除了我上面提到的两种思路,有没有更简单或者直接的方法来解决这个问题?比如一些我不熟悉的函数,或者一些 trick。
char *buf = (char *) calloc(f_sie, sizeof(char)); 中的 f_size
我平时做这件事都是用C++ STL的vector,编码很简单,可能底层效率不高
@Iron_Feet
拷贝粘贴时候发生的一点儿奇怪问题,更正过来了。
这样想改变字符串长度就难了
主要问题是C语言没有动态数组才搞的这么麻烦,可以封装一个动态增长的数组,然后用getc一个字符一个字符的读入,读到换行为止。好处是得到了动态数组这么一个组件,还可以用在其他类似的地方。
array是一个字符指针数组吧?看了半天还以为要分配两个文件大小的空间,其实就第一次全部将文件读进来时分配了一次。
可以考虑mmap
如果文件中有空行,程序就出问题啦。
这个不用free内存吗。不会造成内存泄露吗?
这只是一个函数,不是程序,所以free要放到外面来做啦。
C语言啊
文件大小已知情况下一次性读进缓存是效率最好的,但如果输入是动态的(比如交互),那就必须逐个字符读取了,而且还得用realloc,不过每次重分配的size也可以是动态扩充的。
感觉有个隐藏bug,如果文本行末尾是个反斜杠''那么应该解释为行接续符号,你的程序计算行数就错误了。