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
相关产品推荐
相关产品推荐

