虽然在上一篇文章中说到了相关内容,但是不够具体,本文我们把这一部分的代码单独分析,更清楚的分析源代码中的文件引用方式。
先引出两个结构体,定义在include/darknet.h中:
typedef struct node{
void *val;
struct node *next;
struct node *prev;
} node;
typedef struct list{
int size;
node *front;
node *back;
} list;
这两个结构体用于文件内容的引用,node是节点,存储的是文件中的一行数据,但是并不是以直接字符串的形式,而是一个指针(void *val)指向数据行的首地址。
node是节点是数据行,是文件的组成元素,文件是由node节点链接而成的,所以node中的*next,*prev分别指后节点索引,前节点索引。其实这样已经链成了文件索引链,但是不利于引用。为了利于引用,用一个新的结构体存储一个文件链的头节点和尾节点,以及节点数(文件中数据行的数量),这就是list
list中size指节点数,*front为首节点,*back为尾节点
所以读取文件时,创建空的list,每读一行数据,创建一个node,并链接入list
这是文件的引用方式,下面说说文件的读取:
fgetl函数定义在src/utils.h中,在src/utils.c中实现,代码如下:
char *fgetl(FILE *fp)
{
if(feof(fp)) return 0;
//line的大小为512个char
size_t size = 512;
char *line = malloc(size*sizeof(char));
if(!fgets(line, size, fp)){
free(line);
return 0;
}
//获取字符串真实大小,不包含null
size_t curr = strlen(line);
while((line[curr-1] != '\n') && !feof(fp)){
if(curr == size-1){
size *= 2;
line = realloc(line, size*sizeof(char));
if(!line) {
printf("%ld\n", size);
malloc_error();
}
}
size_t readsize = size-curr;
if(readsize > INT_MAX) readsize = INT_MAX-1;
fgets(&line[curr], readsize, fp);
curr = strlen(line);
}
if(line[curr-1] == '\n') line[curr-1] = '\0';
return line;
}
在用惯了其他高级语言后,这样的算法有点让人头大,fgets按字符读取,设置的读取大小为512,因为,虽然文件行理论可以无限大,但是现在的文本文件以1024为界限,满1024就自动添加换行符,512为1024的一般,可以看但如果512个字符依然没有将文件行读全,就扩大到1024的大小再读,这样做为了减少内存和检索时间。读取数据后,strlen返回真实字符串长度,即不包含末尾的null
通过判断最后一个字符是否是换行符来判度文件行是否全部读入,最后返回的是文件行数据的头指针