Ubuntu下C语言系统调用实现逆序复制文件末n行遇空行问题求助
问题分析与修复
你的问题核心是逻辑错误,和open()的mode参数无关(创建文件时用0666是正确的,权限不是导致空行的原因)。目标文件出现n个空行,是因为代码每次循环都重复读取文件的最后一个字符(通常是换行符\n),导致连续写入n个换行,看起来像空行。
代码中的关键错误
- 重复从文件末尾起始位置读取:每次内层循环都执行
lseek(fd1, -1, SEEK_END),意味着每次都从文件末尾往前移动1字节读取字符。但read()会将文件指针向后移动1字节(指向文件末尾),下一次循环又会回到同样的位置,读取同一个字符。如果原文件最后一个字符是\n,就会连续写入n个\n,形成空行。 - 未正确回溯文件指针:找到换行符后,没有将文件指针移动到换行符的前一位,导致下一次循环仍然读取同一个换行符。
- 缺少错误检查:
open()、lseek()、read()、write()等系统调用都可能失败(比如文件打开失败、指针移动超出文件范围),代码未处理这些错误,会隐藏潜在问题。 - 覆盖逻辑错误:原代码中选择覆盖文件时仅用
O_WRONLY,不会清空原有内容,导致旧内容残留。
修复后的代码
以下是修正逻辑后的代码,同时添加了必要的错误检查:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> int main() { int n, fd1, fd2, choice=0; char sPathName[100], dPathName[100], c; off_t pos, start; printf("Enter name of the source file: \n"); scanf("%s", sPathName); while(access(sPathName, F_OK) != 0) { printf("The file \"%s\" does not exist in the given directory!\n", sPathName); printf("Enter name of a valid source file: \n"); scanf("%s", sPathName); } fd1 = open(sPathName, O_RDONLY); if (fd1 == -1) { perror("Failed to open source file"); return 1; } printf("\nEnter the integer value of number of lines, 'n': \n"); scanf("%d", &n); if (n <= 0) { printf("Invalid n value, must be positive\n"); close(fd1); return 1; } printf("\nEnter name of the destination file: \n"); scanf("%s", dPathName); if(access(dPathName, F_OK) != 0) { fd2 = open(dPathName, O_CREAT|O_WRONLY, 0666); if (fd2 == -1) { perror("Failed to create destination file"); close(fd1); return 1; } } else { printf("\nA file by the name \"%s\" already exists in this directory!\nDo you want to:\n1) Overwrite it\n2) Append to it?\nChoice: ", dPathName); scanf("%d", &choice); if(choice == 1) { fd2 = open(dPathName, O_WRONLY|O_TRUNC); if (fd2 == -1) { perror("Failed to open destination file for overwrite"); close(fd1); return 1; } } else if(choice == 2) { fd2 = open(dPathName, O_WRONLY|O_APPEND); if (fd2 == -1) { perror("Failed to open destination file for append"); close(fd1); return 1; } } else { printf("\nInvalid choice! Exiting program...\n"); close(fd1); return 0; } } // 先将指针移到文件末尾 pos = lseek(fd1, 0, SEEK_END); if (pos == -1) { perror("Failed to seek to end of source file"); close(fd1); close(fd2); return 1; } while(n > 0 && pos > 0) { // 从当前位置往回找换行符 start = pos; while (pos > 0) { // 指针往前移1字节 if (lseek(fd1, --pos, SEEK_SET) == -1) { perror("Failed to seek in source file"); goto cleanup; } if (read(fd1, &c, 1) != 1) { perror("Failed to read source file"); goto cleanup; } // 移回刚才的位置,因为read会让指针前进 if (lseek(fd1, pos, SEEK_SET) == -1) { perror("Failed to seek back"); goto cleanup; } if (c == '\n') { // 找到换行符,从下一个位置开始是当前行的内容 pos++; break; } } // 读取从pos到start的内容,写入目标文件 while (pos < start) { if (read(fd1, &c, 1) != 1) { perror("Failed to read line content"); goto cleanup; } if (write(fd2, &c, 1) != 1) { perror("Failed to write to destination file"); goto cleanup; } pos++; } // 写入换行符(如果不是文件开头的行) if (pos != start) { write(fd2, "\n", 1); } // 跳过当前找到的换行符,准备找下一行 if (pos > 0) { pos--; } n--; } cleanup: close(fd1); close(fd2); return 0; }
修复要点说明
- 初始化文件指针到文件末尾:先通过
lseek(fd1, 0, SEEK_END)获取文件末尾位置,之后从该位置逐步向前回溯,避免重复读取同一位置。 - 正确查找行边界:从当前位置向前逐个字符查找换行符,找到后记录行的起始位置,然后从起始位置到原位置的内容读取并写入目标文件,保证每行内容顺序正确。
- 处理文件开头边界:当回溯到文件开头时,停止查找,避免指针越界。
- 添加错误检查:对所有系统调用的返回值进行检查,遇到错误时及时关闭文件并退出,避免资源泄漏。
- 修正覆盖逻辑:将覆盖文件的参数改为
O_WRONLY|O_TRUNC,确保覆盖时清空原有文件内容。
内容的提问来源于stack exchange,提问作者userUser
相关产品推荐
相关产品推荐

