C语言读取文件到结构体数组后打印输出异常,求助原因与解决
问题分析:赛事文件读取后输出异常
问题背景
将赛事文本文件内容读取到kamp结构体数组后,打印时出现数据错位、乱码等异常输出。
代码与文件信息
原代码
#include <stdio.h> typedef struct kamp{ char ugedag[4]; char dato[6]; double klokkeslaet; char hold[10]; char resultat[6]; int tilskuer; } kamp; int main(void){ FILE *kampe_2022_2023; kampe_2022_2023 = fopen("kampe-2022-2023.txt", "r"); if (kampe_2022_2023 == NULL) { printf("Error reading file"); return 1; } kamp superliga_resultat[5]; for (int i = 0; i < 5; i++) { fscanf(kampe_2022_2023, "%s %s %lf %s %s %d", superliga_resultat[i].ugedag, superliga_resultat[i].dato, &superliga_resultat[i].klokkeslaet, superliga_resultat[i].hold, superliga_resultat[i].resultat, &superliga_resultat[i].tilskuer); } for (int i = 0; i < 5; i++) { printf("%s %s %.2lf %s %s %d\n", superliga_resultat[i].ugedag, superliga_resultat[i].dato, superliga_resultat[i].klokkeslaet, superliga_resultat[i].hold, superliga_resultat[i].resultat, superliga_resultat[i].tilskuer); } fclose(kampe_2022_2023); return(0); }
赛事文件内容
Fre 15/07 19.00 FCM - RFC 1 - 1 7359 Son 17/07 14.00 VFF - AaB 2 - 1 4681 Son 17/07 14.00 LBK - SIF 2 - 2 3871 Son 17/07 16.00 FCK - ACH 0 - 1 22184 Son 17/07 18.00 BIF - AGF 1 - 0 17022
异常输出
Fre 15/07 19.00 FCM - 1 RFC 1 0.00 6417484 1 7359 0.00 ¼wÉýa 6418744 Son 17/07 14.00 VFF - 512 AaB 2 30231459790598215000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.00 ╝îlv\L¡w±îlv³ lv³ 1
错误原因
fscanf的%s匹配逻辑错误%s以空格作为字符串分隔符,但文件中hold字段(如FCM - RFC)、resultat字段(如1 - 1)都包含空格。%s只会读取到第一个空格为止,导致数据彻底错位:比如读取FCM - RFC时,%s只取FCM,剩下的-被当成下一个字段,RFC又占用后续字段位置,最终引发内存越界和乱码。结构体字段长度的潜在风险
hold定义为char hold[10],当前FCM - RFC长度为7(含空格)刚好容纳,但如果出现更长的队名组合会触发缓冲区溢出;resultat定义为char resultat[6],1 - 1长度为4,同样存在溢出风险;- 字符串字段必须预留
\0的存储位置,比如ugedag[4]存储Fre(3字符)没问题,但如果是4字符的星期名就会溢出。
修正方案
方案1:拆分字段(推荐,数据结构更清晰)
将带空格的字段拆分为独立子字段,适配fscanf的分隔逻辑:
修改后的结构体
typedef struct kamp{ char ugedag[4]; char dato[6]; double klokkeslaet; char hold1[6]; // 存储第一个队名,如FCM char hold2[6]; // 存储第二个队名,如RFC int score1; // 第一个队得分 int score2; // 第二个队得分 int tilskuer; } kamp;
修改后的读取逻辑
for (int i = 0; i < 5; i++) { // 用%*s跳过分隔用的'-',匹配文件中的"FCM - RFC"格式 fscanf(kampe_2022_2023, "%s %s %lf %s %*s %s %d %*s %d %d", superliga_resultat[i].ugedag, superliga_resultat[i].dato, &superliga_resultat[i].klokkeslaet, superliga_resultat[i].hold1, superliga_resultat[i].hold2, &superliga_resultat[i].score1, &superliga_resultat[i].score2, &superliga_resultat[i].tilskuer); }
修改后的打印逻辑
for (int i = 0; i < 5; i++) { printf("%s %s %.2lf %s - %s %d - %d %d\n", superliga_resultat[i].ugedag, superliga_resultat[i].dato, superliga_resultat[i].klokkeslaet, superliga_resultat[i].hold1, superliga_resultat[i].hold2, superliga_resultat[i].score1, superliga_resultat[i].score2, superliga_resultat[i].tilskuer); }
方案2:逐行读取后解析(保留原结构体)
先用fgets读取整行,再用sscanf结合宽度限制解析带空格的字段:
#include <stdio.h> #include <string.h> // 需要strrchr处理末尾空格 // 原结构体保持不变 int main(void){ FILE *kampe_2022_2023; kampe_2022_2023 = fopen("kampe-2022-2023.txt", "r"); if (kampe_2022_2023 == NULL) { printf("Error reading file"); return 1; } kamp superliga_resultat[5]; char line[100]; for (int i = 0; i < 5; i++) { fgets(line, sizeof(line), kampe_2022_2023); // 用%9[^0-9]读取hold字段(直到数字前),%5[^0-9]读取resultat字段 sscanf(line, "%s %s %lf %9[^0-9] %5[^0-9] %d", superliga_resultat[i].ugedag, superliga_resultat[i].dato, &superliga_resultat[i].klokkeslaet, superliga_resultat[i].hold, superliga_resultat[i].resultat, &superliga_resultat[i].tilskuer); // 去除字段末尾的多余空格 char *end; end = strrchr(superliga_resultat[i].hold, ' '); if (end) *end = '\0'; end = strrchr(superliga_resultat[i].resultat, ' '); if (end) *end = '\0'; } // 原打印逻辑保持不变 for (int i = 0; i < 5; i++) { printf("%s %s %.2lf %s %s %d\n", superliga_resultat[i].ugedag, superliga_resultat[i].dato, superliga_resultat[i].klokkeslaet, superliga_resultat[i].hold, superliga_resultat[i].resultat, superliga_resultat[i].tilskuer); } fclose(kampe_2022_2023); return(0); }
额外注意事项
- 字符串字段必须预留足够空间存储内容和末尾的
\0; - 避免用
%s读取带空格的内容,防止数据错位; - 读取文件时可检查
fscanf/sscanf的返回值,确认成功读取的字段数,方便排查问题。
内容的提问来源于stack exchange,提问作者Frederik Woloszynski
相关产品推荐
相关产品推荐

