C语言从文件读取结构体后输出异常,请求排查错误
问题分析与修复:C语言文件读取结构体时的数据错位与多余输出问题
看起来你的代码在文件读取逻辑上有几个关键问题,导致了数据错位和最后出现多余的0输出。让我一步步拆解问题并给出修复方案:
核心问题点
1. 读取循环的终止逻辑不可靠
你用while ((c = fgetc(fp)) != EOF)作为循环入口,然后用ungetc把字符放回,这种方式很容易在文件末尾触发额外的循环迭代——当文件最后一个字符被读取后,fgetc返回EOF,循环终止,但如果之前的读取已经完成了所有数据,这时候可能已经多执行了一次循环,填充了一个未初始化的结构体(group默认为0),这就是最后那行0 0 0 0 0输出的原因。
2. 成绩(marks)读取的格式处理错误
你读取前4个成绩用了fscanf("%d ", &stud_list[i].marks[j]),注意%d后面的空格——这个空格会让fscanf跳过所有空白字符(包括空格、换行、制表符等)。如果你的成绩行格式稍有偏差(比如换行提前),就会导致fscanf跳过成绩行的最后一个数字,直接读取下一行的group值,这就是第一个学生的第5个成绩变成12(第二个学生的group)的原因。
3. 写入循环的终止条件不合理
你用while (stud_list[t].group)作为终止条件,默认group永远不会为0,但如果读取错误导致某个结构体的group被设为0,就会错误地停止或输出多余内容。
修复后的代码
读取代码(更稳定的版本)
#define MAX_STUDENTS 100 // 根据你的需求调整最大值 struct Student stud_list[MAX_STUDENTS]; int i = 0; int c; // 用fscanf的返回值判断是否成功读取group(返回1表示读取到一个整数) while (fscanf(fp, "%d", &stud_list[i].group) == 1) { // 跳过group行剩余的字符直到换行 while ((c = fgetc(fp)) != '\n' && c != EOF); // 读取姓氏并去除换行符 if (!fgets(stud_list[i].surname, 50, fp)) break; stud_list[i].surname[strcspn(stud_list[i].surname, "\n")] = 0; // 读取名字 if (!fgets(stud_list[i].name, 50, fp)) break; stud_list[i].name[strcspn(stud_list[i].name, "\n")] = 0; // 读取父称 if (!fgets(stud_list[i].patronymic, 50, fp)) break; stud_list[i].patronymic[strcspn(stud_list[i].patronymic, "\n")] = 0; // 读取入学年份,判断是否成功 if (fscanf(fp, "%d", &stud_list[i].year) != 1) break; // 跳过年份行剩余的字符直到换行 while ((c = fgetc(fp)) != '\n' && c != EOF); // 读取成绩行并解析,避免格式错位 char marks_line[100]; if (!fgets(marks_line, sizeof(marks_line), fp)) break; // 一次性解析5个成绩 if (sscanf(marks_line, "%d %d %d %d %d", &stud_list[i].marks[0], &stud_list[i].marks[1], &stud_list[i].marks[2], &stud_list[i].marks[3], &stud_list[i].marks[4]) != 5) { break; // 如果解析失败,终止循环 } i++; // 防止数组越界 if (i >= MAX_STUDENTS) break; } int student_count = i; // 记录实际读取的学生数量
写入代码(更可靠的版本)
int t, j; // 用实际读取的学生数量作为循环终止条件 for (t = 0; t < student_count; t++) { fprintf(fp, "%d\n", stud_list[t].group); fputs(stud_list[t].surname, fp); fputc('\n', fp); fputs(stud_list[t].name, fp); fputc('\n', fp); fputs(stud_list[t].patronymic, fp); fputc('\n', fp); fprintf(fp, "%d\n", stud_list[t].year); for (j = 0; j < 4; j++) fprintf(fp, "%d ", stud_list[t].marks[j]); fprintf(fp, "%d\n", stud_list[t].marks[4]); }
修复说明
- 读取循环:改用
fscanf的返回值判断是否成功读取group,避免了fgetc+ungetc的不可靠性,同时记录实际读取的学生数量,防止数组越界。 - 成绩读取:先用
fgets读取整个成绩行,再用sscanf解析,不管行内有多少空白字符,都能准确解析出5个成绩,彻底解决了格式错位问题。 - 写入循环:用实际读取的学生数量作为终止条件,不再依赖
group的值,避免了未初始化结构体的输出。
内容的提问来源于stack exchange,提问作者Sherstnyov
相关产品推荐
相关产品推荐

