自定义Linux tail函数实现问题:输出末尾出现多余乱码
解决自定义tail程序输出末尾乱码及行计数错误的问题
你的自定义tail程序确实存在两个核心问题:字节计数逻辑错误和字符串未终止导致的乱码,以下是具体分析和修复方案:
问题分析
字符串未终止导致乱码:
你用栈数组char tail[bytes];存储读取的文件内容,但文件内容本身并不包含C字符串所需的'\0'终止符。printf("%s")会一直读取内存直到遇到'\0',这就会读取到栈上的垃圾数据,表现为末尾的乱码(比如“�E0V”)。行计数逻辑错误:
在get_bytes函数中,bytes++是在回退文件指针后执行的。当找到第n个换行符时,会直接break,导致这个换行符本身没有被计入bytes。比如当执行./tail -1 filename时,如果文件最后一行以'\n'结尾,程序会返回bytes=0,最终读取0字节,完全不输出内容。
修复方案
1. 修正字节计数逻辑
调整get_bytes函数的执行顺序,将bytes++移到读取字节之后,确保每个读取到的字节(包括换行符)都被正确计数:
int get_bytes(int lines, int fd) { int bytes = 0; char buff[1]; while (1) { if (read(fd, buff, 1) == -1){ printf("read failed\n"); return -1; } bytes++; // 先计数当前读取的字节 if (buff[0] == '\n') { lines--; if (lines <= 0) break; } if (lseek(fd, -2, SEEK_CUR) == -1) { // 到达文件开头,退出循环 break; } } return bytes; }
2. 添加字符串终止符并优化内存分配
使用动态内存分配(避免栈溢出风险),读取文件内容后手动添加'\0'终止符,同时修正输出格式避免多输出空行:
// 替换main中原来的tail缓冲区相关代码 char* tail = malloc(bytes + 1); if (!tail) { printf("malloc failed\n"); return EXIT_FAILURE; } if (lseek(fd, -bytes, SEEK_END) == -1) { printf("lseek to start of tail failed\n"); free(tail); return EXIT_FAILURE; } ssize_t read_bytes = read(fd, tail, bytes); if (read_bytes == -1){ printf("read failed\n"); free(tail); return EXIT_FAILURE; } tail[read_bytes] = '\0'; // 添加C字符串终止符 printf("%s", tail); // 不再额外加\n,避免多空行 free(tail);
3. 其他小优化
- 修正
lseek的错误提示,避免重复; - 确保文件描述符和动态内存都被正确释放,防止资源泄漏。
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int get_lines(char** argv); int get_bytes(int lines, int fd); int main(int argc, char* argv[]) { if (argc != 3) { printf("ungültige Anzahl an args\n"); return EXIT_FAILURE; } int lines = get_lines(argv); if (lines == -1) return EXIT_FAILURE; // open file char* path = argv[2]; int fd = open(path, O_RDONLY); if (fd == -1) { printf("file konnte nicht geöffnet werden oder existiert nicht\n"); return EXIT_FAILURE; } // get stat of file struct stat infos; if (stat(path, &infos) == -1) { printf("stat failed\n"); return EXIT_FAILURE; } // set offset to last byte if (lseek(fd, -1, SEEK_END) == -1) { printf("lseek to last byte failed\n"); close(fd); return EXIT_FAILURE; } // determine number of bytes corresponding to number of lines int bytes = get_bytes(lines, fd); // printf("lines: %d\nbytes: %d\n", lines, bytes); if (bytes == -1) { close(fd); return EXIT_FAILURE; } // set offset to beginning of tail and save tail of file in buffer tail char* tail = malloc(bytes + 1); if (!tail) { printf("malloc failed\n"); close(fd); return EXIT_FAILURE; } if (lseek(fd, -bytes, SEEK_END) == -1) { printf("lseek to start of tail failed\n"); free(tail); close(fd); return EXIT_FAILURE; } ssize_t read_bytes = read(fd, tail, bytes); if (read_bytes == -1){ printf("read failed\n"); free(tail); close(fd); return EXIT_FAILURE; } tail[read_bytes] = '\0'; printf("%s", tail); free(tail); close(fd); return 0; } int get_lines(char** argv) { // cast string without - to int char* substr = &argv[1][1]; int lines = atoi(substr); if (lines == 0) { printf("-n mit n = Anzahl Zeilen\n"); return -1; } return lines; } int get_bytes(int lines, int fd) { int bytes = 0; char buff[1]; while (1) { if (read(fd, buff, 1) == -1){ printf("read failed\n"); return -1; } bytes++; if (buff[0] == '\n') { lines--; if (lines <= 0) break; } if (lseek(fd, -2, SEEK_CUR) == -1) { // n bytes was bigger than bytes of file break; } } return bytes; }
测试验证
现在执行./tail -4 tail.c,输出末尾不会再出现乱码,且能正确打印最后4行内容。
内容的提问来源于stack exchange,提问作者Simon Rechermann
相关产品推荐
相关产品推荐

