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

C语言文件字符串更新时修改目标行后其余数据丢失问题

问题描述
  • 功能目标:用C语言实现任务文件更新函数,交互逻辑为:用户输入任务唯一ID,程序匹配到文件内对应ID的任务行后,打印该行的任务名称、日期、分类等字段,支持用户修改上述内容后写回文件。
  • 测试用tasks.txt文件内容:
02 AnnesBirthday 17 12 2022 Event
03 JacksBirthday 12 12 2022 Event
  • 异常现象:输入ID 02完成对应任务修改后,文件内其余记录(如ID为03的任务)会被完全擦除。
  • 原有问题代码:
void UpdateTask() {

    char taskname[100];

    int i,idnumber,date, month, year, choice;

    char category[20];

    FILE *f = fopen("tasks.txt", "r");

    FILE *ft = fopen("temp.txt", "w");

    printf("\n********");

    printf("\nUpdate tasks here");

    printf("\n********");

    printf("\nEnter task number: ");

    scanf("%d", &i);

    while (fscanf(f, "%d", &idnumber) != EOF) {
        fscanf(f,"%d %s %d %d %d %s\n",idnumber,taskname, date, month, year, category);
        if (i == idnumber) {

            printf("\nUpdating -> %s %d %d %d %s", taskname, date, month, year, category);
            printf("\nEnter a new task name: ");
            fflush(stdin);
            gets(taskname);
            printf("\nEnter a new day (dd/mm/yy): ");
            scanf("%d",&date);
            printf("\nEnter a new month (dd/mm/yy): ");
            scanf("%d",&month);
            printf("\nEnter a new year(dd/mm/yy): ");
            scanf("%d",&year);
            printf("\nEnter a new category (max 49 characters): ");
            fflush(stdin);
            gets(category);
            fprintf(ft, "%d %s %d %d %d %s\n", idnumber, taskname, date, month, year, 
             category);
            fclose(ft);
            fclose(f);
            remove("tasks.txt");
            rename("temp.txt", "tasks.txt");



    }
    else
        fprintf(ft, "%d%s", i,taskname, date, month, year, category );
}
问题根因

代码存在4个直接触发异常的核心错误:

  • 提前终止文件遍历流程:匹配到目标ID后,刚写完修改后的单条记录,就直接关闭文件、删除原文件、重命名临时文件,此时while循环还没处理完后续行,ID 03这类后续任务根本没有被读取、写入临时文件,直接丢失。
  • 文件读取逻辑错误:while循环判断条件里已经读取了一次idnumber,循环内又重复调用fscanf读取id,会直接跳行;且fscanf读取int类型参数时没有传入变量地址(缺&符号),属于未定义行为,会读错数据甚至触发程序崩溃。
  • 非目标行写入逻辑错误:else分支的fprintf格式串仅配置了%d%s两个占位符,日期、分类等后续参数没有对应占位;且写入的ID是用户输入的目标ID,不是当前遍历到的任务ID,即使写入成功也是错误数据。
  • 输入处理不符合C标准:gets()函数因存在缓冲区溢出风险,早已被C标准废弃移除;fflush(stdin)属于未定义行为,不同编译环境下表现不可控,很容易出现输入读取错乱。
修复后可运行代码
#include <stdio.h>
#include <string.h>

void UpdateTask() {
    char taskname[100];
    int i, idnumber, date, month, year;
    char category[20];

    // 增加文件打开校验,避免空指针崩溃
    FILE *f = fopen("tasks.txt", "r");
    if (f == NULL) {
        printf("打开任务文件失败\n");
        return;
    }
    FILE *ft = fopen("temp.txt", "w");
    if (ft == NULL) {
        printf("创建临时文件失败\n");
        fclose(f);
        return;
    }

    printf("\n********");
    printf("\nUpdate tasks here");
    printf("\n********");
    printf("\nEnter task number: ");
    scanf("%d", &i);
    // 读走scanf残留在输入缓冲区的换行符,避免后续输入读空
    while (getchar() != '\n');

    // 修正读取逻辑:一次读取整行所有字段,不重复读取ID,int参数全部传地址
    while (fscanf(f, "%d %99s %d %d %d %19s", &idnumber, taskname, &date, &month, &year, category) != EOF) {
        if (i == idnumber) {
            printf("\nUpdating -> %s %d %d %d %s", taskname, date, month, year, category);
            
            printf("\nEnter a new task name: ");
            // 替换废弃的gets,限制读取长度避免缓冲区溢出
            fgets(taskname, 100, stdin);
            // 去掉fgets自动读入的末尾换行符
            taskname[strcspn(taskname, "\n")] = 0;

            printf("\nEnter a new day (dd/mm/yy): ");
            scanf("%d", &date);
            printf("\nEnter a new month (dd/mm/yy): ");
            scanf("%d", &month);
            printf("\nEnter a new year(dd/mm/yy): ");
            scanf("%d", &year);
            // 再次读走缓冲区残留换行
            while (getchar() != '\n');

            printf("\nEnter a new category (max 19 characters): ");
            fgets(category, 20, stdin);
            category[strcspn(category, "\n")] = 0;

            // 写入修改后的目标记录,不在此处关闭文件
            fprintf(ft, "%d %s %d %d %d %s\n", idnumber, taskname, date, month, year, category);
            printf("任务更新完成\n");
        } else {
            // 非目标行原样写入临时文件,补全所有字段占位,使用当前行的真实ID
            fprintf(ft, "%d %s %d %d %d %s\n", idnumber, taskname, date, month, year, category);
        }
    }

    // 所有行全部处理完成后,再关闭文件、执行替换操作
    fclose(ft);
    fclose(f);
    remove("tasks.txt");
    rename("temp.txt", "tasks.txt");
}
修复要点说明
  • 将文件关闭、原文件删除、临时文件重命名的逻辑移到while循环外部,保证所有任务记录(不管是修改的还是未修改的)都完整写入临时文件后再执行替换,从根源解决后续记录丢失的问题。
  • 修正fscanf的传参与读取逻辑:int类型参数全部传入地址,去掉重复读取ID的逻辑,给字符串读取加长度限制,避免跳行、读错数据、缓冲区溢出。
  • 修正非目标行的写入逻辑,补全所有字段的格式占位,写入当前遍历到的任务ID,保证未修改的记录可以原样保留。
  • 移除所有不符合C标准的输入处理逻辑:删除fflush(stdin),用循环读取的方式清理缓冲区残留换行;替换废弃的gets()为带长度限制的fgets(),同时手动去除读取到的末尾换行,避免文件出现多余空行。
  • 增加文件打开失败的异常判断,避免文件不存在时触发空指针崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:42:16