You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

修复要点说明

  1. 初始化文件指针到文件末尾:先通过lseek(fd1, 0, SEEK_END)获取文件末尾位置,之后从该位置逐步向前回溯,避免重复读取同一位置。
  2. 正确查找行边界:从当前位置向前逐个字符查找换行符,找到后记录行的起始位置,然后从起始位置到原位置的内容读取并写入目标文件,保证每行内容顺序正确。
  3. 处理文件开头边界:当回溯到文件开头时,停止查找,避免指针越界。
  4. 添加错误检查:对所有系统调用的返回值进行检查,遇到错误时及时关闭文件并退出,避免资源泄漏。
  5. 修正覆盖逻辑:将覆盖文件的参数改为O_WRONLY|O_TRUNC,确保覆盖时清空原有文件内容。

内容的提问来源于stack exchange,提问作者userUser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 10:07:48