C语言任务管理器:结构体数据文件存读异常(存正常读失效)
任务管理器数据加载与保存问题
我正在开发一款任务管理器,用于存储任务与员工信息,已定义两个结构体。需求为:启动程序时从文件将数据加载至结构体,关闭程序时将结构体数据保存回文件。目前saveData函数可正常运行,但GetData函数无法正常工作;此外,即便文件打开时未将数据加载到结构体,关闭程序时仍会保存数据且不会覆盖原有内容。
现有代码
从文件加载数据的函数
void GetData(){ FILE *fptr = fopen("Manager_Data.txt", "r"); for (int i = 0;i < TAM_EMP; i++) { fscanf(fptr, "%d,%s,%s,%d,%s,%s,%s,%d ", &employees[i].ID, employees[i].name, employees[i].date, &employees[i].phone, employees[i].email, employees[i].local, employees[i].depart, &employees[i].availability); } fclose(fptr); }
将数据保存至文件的函数
void saveData(){ FILE *fptr = fopen("Manager_Data.txt", "w"); for (int i = 0;i < TAM_EMP; i++) { fprintf(fptr, "%d,%s,%s,%d,%s,%s,%s,%d ", employees[i].ID, employees[i].name, employees[i].date, employees[i].phone, employees[i].email, employees[i].local, employees[i].depart, employees[i].availability); } fclose(fptr); }
问题分析与修复方案
1. GetData函数失效的原因及修复
- 未处理文件打开失败:若文件不存在或无读取权限,
fopen返回NULL,直接调用fscanf会触发程序崩溃,必须先校验文件指针有效性。 - 格式字符串匹配错误:原代码用
%s读取字段,会在空白字符处停止读取,若字段包含空格会导致数据截断;同时格式串中的换行符匹配逻辑不稳定,易引发读取错位。 - 未校验读取结果:无法确认每行8个字段是否全部读取成功,可能出现部分数据加载失败的情况。
修复后的GetData函数:
void GetData(){ FILE *fptr = fopen("Manager_Data.txt", "r"); if (fptr == NULL) { // 文件不存在时初始化空结构体,避免后续保存垃圾数据 for (int i = 0; i < TAM_EMP; i++) { employees[i].ID = 0; employees[i].phone = 0; employees[i].availability = 0; // 初始化字符串字段为空 memset(employees[i].name, 0, sizeof(employees[i].name)); memset(employees[i].date, 0, sizeof(employees[i].date)); memset(employees[i].email, 0, sizeof(employees[i].email)); memset(employees[i].local, 0, sizeof(employees[i].local)); memset(employees[i].depart, 0, sizeof(employees[i].depart)); } return; } int i = 0; // 逐行读取直到文件结束或达到最大员工数,校验读取字段数 while (i < TAM_EMP && fscanf(fptr, "%d,%[^,],%[^,],%d,%[^,],%[^,],%[^,],%d ", &employees[i].ID, employees[i].name, employees[i].date, &employees[i].phone, employees[i].email, employees[i].local, employees[i].depart, &employees[i].availability) == 8) { i++; } // 剩余未读取的位置初始化为空 for (; i < TAM_EMP; i++) { employees[i].ID = 0; employees[i].phone = 0; employees[i].availability = 0; memset(employees[i].name, 0, sizeof(employees[i].name)); memset(employees[i].date, 0, sizeof(employees[i].date)); memset(employees[i].email, 0, sizeof(employees[i].email)); memset(employees[i].local, 0, sizeof(employees[i].local)); memset(employees[i].depart, 0, sizeof(employees[i].depart)); } fclose(fptr); }
说明:
- 新增文件打开失败的判断逻辑,初始化空结构体避免垃圾数据流入。
- 用
%[^,]替代%s,确保完整读取逗号分隔的字段(前提是字段本身不含逗号)。 - 循环读取时校验
fscanf返回值,确保每次成功读取8个字段。
2. 保存时不覆盖原有内容的问题及修复
当前saveData用"w"模式打开文件,该模式会清空文件再写入,出现不覆盖的情况大概率是文件打开失败未处理:若fopen返回NULL,后续fprintf不会执行,原有内容自然保留。
修复后的saveData函数:
void saveData(){ FILE *fptr = fopen("Manager_Data.txt", "w"); if (fptr == NULL) { // 可添加错误提示,比如printf("无法保存文件! "); return; } for (int i = 0; i < TAM_EMP; i++) { // 只保存ID不为0的有效员工数据,避免写入空数据 if (employees[i].ID != 0) { fprintf(fptr, "%d,%s,%s,%d,%s,%s,%s,%d ", employees[i].ID, employees[i].name, employees[i].date, employees[i].phone, employees[i].email, employees[i].local, employees[i].depart, employees[i].availability); } } fclose(fptr); }
说明:
- 新增文件打开失败的判断,避免无意义操作。
- 仅保存ID不为0的有效员工数据,减少文件中的无效内容。
额外建议
- 定义结构体时,确保字符串字段有足够长度,避免缓冲区溢出。
- 可考虑使用二进制文件读写(
fread/fwrite),比文本格式更可靠,尤其适合结构体数据。 - 程序启动时必须调用
GetData,退出时调用saveData,确保数据加载和保存时机正确。
内容的提问来源于stack exchange,提问作者Antônio Velez
相关产品推荐
相关产品推荐

