C语言sscanf读取文件含空格字符串字段截断问题求解
问题根因
%s格式符默认以任意空白字符(空格、制表符等)作为读取终止边界,因此包含空格的院校名会在第一个空格处被截断,仅能读取到"Università"。- 之前尝试的
[^\t\n]格式符以制表符、换行作为终止边界,会把行尾的平均分字段一并吞入,本质是没有定位到院校字段的正确结束位置。 - 原代码存在三个隐性bug:
fopen打开模式写为"r "末尾多了空格,可能导致文件打开行为异常sscanf读取double类型时误用%f格式符,%f仅对应float*类型,读取double需使用%lf- 字符串读取未做长度限制,存在缓冲区溢出风险
实现思路
单行字段结构固定为「无空格姓名 -> 整数年龄 -> 可含空格的院校名 -> 浮点数平均分」,最稳妥的解析逻辑是从行两端固定无空格的字段入手,中间剩余部分就是院校名:
- 先从行首读取无空格的姓名字符串、整数年龄,记录读取完成后的偏移位置
- 从行尾向前遍历,定位最后一个浮点数(平均分)的起始位置,读取平均分
- 两个位置中间的字符串,去除首尾多余空格后就是完整的院校名
这种方式不会因为院校名包含空格、数字等特殊字符出现解析错误。如果确认院校名不会出现数字,也可以直接用调整后的sscanf格式串快速实现:" %29s %d %29[^0-9] %lf",读完院校字段后手动去除末尾的空格即可。
修正后完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> typedef struct{ char nome[30]; int eta; char universita[30]; double media; }Studente; // 工具函数:去除字符串首尾空白字符 void trim(char* str) { if (str == NULL) return; // 去除首部空格 while(isspace((unsigned char)*str)) str++; if (*str == 0) return; // 去除尾部空格 char* end = str + strlen(str) - 1; while(end > str && isspace((unsigned char)*end)) end--; end[1] = '\0'; } int main(int argc, char** argv){ FILE* fp; Studente s; // 修正fopen模式,去掉多余空格 if((fp=fopen("studenti.txt", "r")) == NULL){ perror("fopen"); exit(1); } char buffer[200]; while(fgets(buffer, sizeof(buffer), fp)){ int offset = 0; // 读姓名、年龄,通过%n记录读取完成后的偏移位置,加长度限制防止缓冲区溢出 sscanf(buffer + offset, " %29s %d %n", s.nome, &s.eta, &offset); // 从行尾定位平均分的起始位置 int len = strlen(buffer); int media_start = len - 1; // 跳过行尾空白字符 while(media_start >=0 && isspace((unsigned char)buffer[media_start])) media_start--; // 向前遍历找到浮点数的开头(匹配数字、小数点) while(media_start >=0 && (isdigit((unsigned char)buffer[media_start]) || buffer[media_start] == '.')) { media_start--; } media_start++; // 读取平均分 sscanf(buffer + media_start, "%lf", &s.media); // 截取中间部分作为院校名 int uni_len = media_start - offset; strncpy(s.universita, buffer + offset, uni_len); s.universita[uni_len] = '\0'; trim(s.universita); // 清除首尾多余空格 // 输出解析结果 printf("Lo studente chiama: %s\n", s.nome); printf("Ha %d anni\n", s.eta); printf("Frequenta %s\n", s.universita); printf("Ha una media del %lf\n", s.media); printf("\n"); } fclose(fp); return 0; }
效果说明
测试输入行Luca 21 Università di Palermo 22.3时,解析结果完全符合预期:
- 姓名:Luca
- 年龄:21
- 院校:Università di Palermo
- 平均分:22.3
该实现同时规避了缓冲区溢出、格式符不匹配等隐性问题,对包含数字的院校名也能正常解析。
内容的提问来源于stack exchange,提问作者ElAzazel
相关产品推荐
相关产品推荐

