基于getline的自定义tail命令如何调整tailbuf的行存储顺序?
问题
希望不使用lseek和fseek,仅通过getline实现自定义tail命令,已编写如下代码,但希望tailbuf数组的存储顺序满足:tailbuf[9]存储文件最后一行,tailbuf[8]存储倒数第二行,以此类推。请帮助修改代码实现该需求。
#include<stdio.h> #include<stdlib.h> char ** lastnumlines(FILE *fp, unsigned int num) { int count = num; int n, i, iNo1 = 0, iNo2= 0,z = 0; size_t MAXSIZE = 1024; char **tailbuf=calloc(count, sizeof(char *)); for (i = 0; i < count; i++) { tailbuf[i] = calloc(MAXSIZE, sizeof(char)); } while (getline(&tailbuf[iNo2], &MAXSIZE, fp) != EOF) { iNo2 = (iNo2 + 1) % count; if (iNo2 == iNo1) { iNo1 = (iNo1 + 1) % count; } } i = iNo2; int k = 0; do{ printf("%s\n",tailbuf[i]); i = (i+1) % count; }while (i != iNo2); free(tailbuf); } int main() { char *filename = "demo1.txt"; FILE *fp = fopen(filename,"r"); if(fp == NULL) { printf("Unable to open file!\n"); exit(1); } lastnumlines(fp,10); fclose(fp); return 0; }
解决方案
要实现tailbuf数组从后往前存储最后N行(最后一行在tailbuf[num-1],倒数第二行在tailbuf[num-2],以此类推),可以调整循环写入的索引逻辑,让新读取的行始终向数组末尾方向覆盖,最后将循环队列的内容按目标顺序整理到位。
关键修改点
- 改用倒序循环索引写入新行,确保最新行始终占据数组的末尾方向
- 读取完成后,将循环队列中的内容重新排列成目标顺序
- 修复原代码的内存泄漏问题(原代码未释放每个行缓冲区的内存)
修改后的完整代码
#include<stdio.h> #include<stdlib.h> #include<string.h> char ** lastnumlines(FILE *fp, unsigned int num) { int count = num; int i, write_idx = count; // 初始索引指向数组末尾的下一个位置(模运算后对应count-1) size_t MAXSIZE = 1024; char **tailbuf = calloc(count, sizeof(char *)); // 初始化每个行缓冲区,处理内存分配失败的情况 for (i = 0; i < count; i++) { tailbuf[i] = calloc(MAXSIZE, sizeof(char)); if (tailbuf[i] == NULL) { for (int j = 0; j < i; j++) free(tailbuf[j]); free(tailbuf); return NULL; } } // 用临时缓冲区读取行,避免干扰tailbuf的内存管理 char *line = NULL; size_t line_size = 0; ssize_t read_len; while ((read_len = getline(&line, &line_size, fp)) != EOF) { // 倒序计算写入索引:每次往前移动一位,循环覆盖最旧的行 write_idx = (write_idx - 1 + count) % count; // 复制新行到对应位置,确保字符串终止 strncpy(tailbuf[write_idx], line, read_len); tailbuf[write_idx][read_len] = '\0'; } free(line); // 释放getline分配的临时缓冲区 // 将循环队列内容整理成目标顺序:tailbuf[count-1]存最后一行 char **sorted_buf = calloc(count, sizeof(char *)); if (sorted_buf == NULL) { for (i = 0; i < count; i++) free(tailbuf[i]); free(tailbuf); return NULL; } for (i = 0; i < count; i++) { int src_idx = (write_idx + i) % count; sorted_buf[count - 1 - i] = tailbuf[src_idx]; tailbuf[src_idx] = NULL; // 标记为已转移,避免重复释放 } // 释放原指针数组(元素已转移到sorted_buf) free(tailbuf); // 打印验证顺序 for (i = 0; i < count; i++) { if (strlen(sorted_buf[i]) > 0) { printf("%s", sorted_buf[i]); } } return sorted_buf; // 返回整理后的缓冲区,由调用者负责释放 } int main() { char *filename = "demo1.txt"; FILE *fp = fopen(filename,"r"); if(fp == NULL) { printf("Unable to open file!\n"); exit(1); } char **result = lastnumlines(fp,10); // 释放返回的缓冲区 if (result != NULL) { for (int i = 0; i < 10; i++) free(result[i]); free(result); } fclose(fp); return 0; }
代码说明
- 写入逻辑:通过
write_idx = (write_idx - 1 + count) % count实现倒序循环写入,新行始终覆盖数组中最旧的位置,最新行的位置逐步向数组末尾移动。 - 顺序整理:读取完成后,从
write_idx开始遍历循环队列,将内容反向复制到新数组,确保tailbuf[9]对应最后一行,tailbuf[8]对应倒数第二行。 - 内存管理:修复了原代码的内存泄漏,所有分配的内存都能被正确释放,同时处理了内存分配失败的异常情况。
内容的提问来源于stack exchange,提问作者Mr_Nobody99
相关产品推荐
相关产品推荐

