C语言文件读取逻辑错误排查:fgets读取员工数据异常
员工数据文件读取异常问题排查与解决
问题背景
用户编写了C语言函数读取包含字符串和浮点值的员工数据文件,运行后出现输出异常,怀疑问题与fgets、strtok的使用有关,且希望仅使用fgets实现读取逻辑,不使用fscanf。
用户原代码
#include <stdio.h> #include <string.h> typedef struct { char Name[100]; char Title; double Hours_Worked; double Pay_Rate; double Payment; } Employee; void Read_Records_From_File(FILE *Read_File, Employee *Payroll) { int Index; char First_Name[100], Last_Name[100]; Index = 0; while (!feof(Read_File)) { fgets(Payroll[Index].Name, 100, Read_File); strtok(Payroll[Index].Name, "\n"); fscanf(Read_File, "%c", &Payroll[Index].Title); fscanf(Read_File, "%lf", &Payroll[Index].Hours_Worked); fscanf(Read_File, "%lf", &Payroll[Index].Pay_Rate); printf("Name : %s", Payroll[Index].Name); printf("\nTitle : %c", Payroll[Index].Title); printf("\nHours Worked : %lf", Payroll[Index].Hours_Worked); printf("\nPay Rate : %lf", Payroll[Index].Pay_Rate); puts("\n"); Index += 1; } } int main(void) { FILE *Read_File; Employee Payroll[100]; Read_File = fopen("Payroll.txt", "r"); Read_Records_From_File(Read_File, &Payroll); }
数据文件内容(Payroll.txt)
SurName1 FirstName1 B 40.0 12.0 SurName2 FirstName2 M 35.0 20.0
异常输出
Name : SurName1 FirstName1 Title : B Hours Worked : 40.000000 Pay Rate : 12.000000 Name : Title : S Hours Worked : 0.000000 Pay Rate : 0.000000 Name : urName2 FirstName2 Title : M Hours Worked : 35.000000 Pay Rate : 20.000000 Program ended with exit code: 0
问题原因分析
feof循环条件错误:while (!feof(Read_File))会导致多执行一次循环。feof仅在尝试读取超出文件末尾时才会置位,因此最后一次循环会读取无效数据,出现空Name和错误的Title。fscanf残留换行符干扰:fscanf读取字符和数值后,文件指针会停留在换行符前,下一次fgets会直接读取这个空换行,导致Name字段为空;后续读取Title时,会读到下一行的第一个字符(如S),引发连锁错误。strtok用法不严谨:strtok(Payroll[Index].Name, "\n")虽然能去除换行符,但如果fgets读取的字符串刚好填满缓冲区且没有换行,该操作会破坏字符串;且未保存返回值,写法不安全。
解决方案(仅用fgets实现)
修改读取逻辑,全部用fgets读取每行数据,再用sscanf解析数值,同时修正循环条件和换行符处理:
#include <stdio.h> #include <string.h> typedef struct { char Name[100]; char Title; double Hours_Worked; double Pay_Rate; double Payment; } Employee; void Read_Records_From_File(FILE *Read_File, Employee *Payroll) { int Index = 0; char buffer[100]; // 临时缓冲区存储非字符串字段 // 用fgets返回值判断是否读取成功,替代feof while (fgets(Payroll[Index].Name, sizeof(Payroll[Index].Name), Read_File)) { // 用strcspn安全去除换行符:找到换行符位置,替换为'\0' Payroll[Index].Name[strcspn(Payroll[Index].Name, "\n")] = '\0'; // 读取Title行 if (!fgets(buffer, sizeof(buffer), Read_File)) break; // 从缓冲区解析单个字符,忽略换行 sscanf(buffer, "%c", &Payroll[Index].Title); // 读取Hours_Worked行 if (!fgets(buffer, sizeof(buffer), Read_File)) break; sscanf(buffer, "%lf", &Payroll[Index].Hours_Worked); // 读取Pay_Rate行 if (!fgets(buffer, sizeof(buffer), Read_File)) break; sscanf(buffer, "%lf", &Payroll[Index].Pay_Rate); // 输出验证 printf("Name : %s\n", Payroll[Index].Name); printf("Title : %c\n", Payroll[Index].Title); printf("Hours Worked : %.2lf\n", Payroll[Index].Hours_Worked); printf("Pay Rate : %.2lf\n\n", Payroll[Index].Pay_Rate); Index++; // 防止数组越界 if (Index >= 100) break; } } int main(void) { FILE *Read_File; Employee Payroll[100]; Read_File = fopen("Payroll.txt", "r"); // 增加文件打开失败检查 if (!Read_File) { printf("无法打开文件\n"); return 1; } Read_Records_From_File(Read_File, Payroll); fclose(Read_File); // 关闭文件 return 0; }
修改要点说明
- 循环条件修正:用
fgets的返回值作为循环判断,当返回NULL时立即终止循环,避免读取无效数据。 - 统一用
fgets读取:所有字段都通过fgets读取整行,彻底解决fscanf残留换行符的问题;非字符串字段先存入临时缓冲区,再用sscanf解析。 - 安全去除换行符:用
strcspn替代strtok,直接定位换行符位置并替换为结束符,无论fgets是否读到换行都能安全处理。 - 增加错误检查:判断文件是否成功打开,防止空指针操作;读取每行时检查
fgets返回值,避免文件中途结束导致的错误;限制数组索引,防止越界。
内容的提问来源于stack exchange,提问作者TheRecon007
相关产品推荐
相关产品推荐

