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

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]);
}

修复说明

  1. 读取循环:改用fscanf的返回值判断是否成功读取group,避免了fgetc+ungetc的不可靠性,同时记录实际读取的学生数量,防止数组越界。
  2. 成绩读取:先用fgets读取整个成绩行,再用sscanf解析,不管行内有多少空白字符,都能准确解析出5个成绩,彻底解决了格式错位问题。
  3. 写入循环:用实际读取的学生数量作为终止条件,不再依赖group的值,避免了未初始化结构体的输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:17:50