fgets(char* buffer, int n, File* fp); 第一个参数类型是char*,一般是char数组,用于缓存读取的结果;第二个参数是读取的长度n。fgets会预留最后一位byte给空字符,所以buffer[n-1]一定是空字符(buffer[n-1] == '\0')。 File*是文件指针或者标准输入。如果读取n-1个字符过程中,遇到了换行符\n, 本次读取结束,所以fgets也能理解为按行读。
当文件某行的字符数加上换行符的数量,<=n-1个;buffer只会被填 字符数 + 2(1个换行符和1个空字符)个bytes, buffer的最后一个byte是空字符;要注意索引(index) 从字符数+2 到 buffer_size - 1的中间的区域没有被fgets写入,如果buffer用malloc分配的话,且调用fgets前没有初始化逻辑,这块区域未被初始化(uninitialized);对这块区域进行读取,计算,判断等属于未定义的行为,所以不能通过索引中间的区域去做逻辑判断。
要想知道给定buffer的长度是否可以装下这行,可以通过memchr(buffer, ‘\n’, strlen(buffer)) ; 从buffer头开始的strlen(buffer)个字节里,找换行符’\n‘首次出现的位置。如果结果返回NULL,说明读出的字符里没有换行符,这一行没有被读完。如果不是NULL,说明这一行被读完了。
当文件某行的字符数加上换行符的数量,>n-1个;由于buffer的最后一位一定是null byte,换行符没有被读取。memchr(buffer, ‘\n’, strlen(buffer))一定是NULL。 要对这一行再次进行读取;首先通过reallocate分配一个更大的,具有new_capacity个bytes的空间,得到新的buffer: new_buffer。 此时注意new_buffer的数据和原来的buffer一样,而更好的是再次调用fgets(some-buffer-pointer, some-size, fp)时,fp的指针位置由于之前的fgets调用已经发生了变化,会从下一个还未被读取的字符开始。而下一个未被读取的字符,正好要填在new_buffer[old_capacity-1]上,所以some-buffer-pointer传new_buffer + (old_capacity - 1);some-size要能覆盖从 old_capacity - 1 到 new_capacity - 1 的索引(index) → some-size传new_capacity - old_capacity + 1。fgets执行完后,可以再次memchr(new_buffer, ‘\n’, strlen(new_buffer))判断有没有读出换行符,没有换行符则需要重复上述reallocate,fgets的步骤,直到读出了换行符,memchr(new_buffer, ‘\n’, strlen(new_buffer))不为空。具体代码如下:
char *read_line(FILE *fp)**
{
size_t capacity = 128;
char *line = malloc(capacity * sizeof(char));
if (line == NULL){
return NULL;
}
if (fgets(line, capacity, fp) == NULL){
free(line);
return NULL;
}
char *newLine = memchr(line, '\n', strlen(line));
while (newLine == NULL){
// fgets doesnt meet \n
// 扩容1.5倍
size_t oldCap = capacity;
capacity += (capacity + 1) / 2;
line = realloc(line, sizeof(char) * capacity);
// line[old capacity-1] = '\0';
// fgets to cover index from old capacity - 1 to new capacity - 1
char *concatStart = line + (oldCap -1);
fgets(concatStart, capacity - oldCap + 1, fp);
newLine = memchr(concatStart, '\n', strlen(concatStart));
}
// 根据字符的实际长度缩小空间
size_t actualLen = newLine - line + 2;
line = realloc(line, sizeof(char) * actualLen);
return line;
}