C语言从文件加载字符串到结构体数组异常:仅加载1个城市的问题排查与修复
程序问题分析与修复方案
你遇到的核心问题是读取文件时的格式字符串完全错误,导致只加载了1个城市,还出现了奇怪的垃圾值。咱们一步步拆解问题,再给出修复方案:
为什么只加载了1个城市?
你的load函数里用了这个fscanf语句:
while (fscanf(fp, "%[^,]s %d\n", cities[i].name, &cities[i].number_of_citizen)) {
这里有两个致命错误:
- 格式字符串不符合文件内容:你写入文件的是空格分隔的内容(比如
Sarajevo 345123),但%[^,]的意思是“读取所有非逗号的字符”——可你的文件里根本没有逗号!所以这个格式会一直读取,直到文件结束,把整个文件的内容都塞进第一个城市的name字段里。 - 格式字符串语法错误:
%[^,]本身就是完整的转换说明符,后面不需要额外的s,这会导致fscanf尝试读取一个s字符,进一步加剧读取失败。
当fscanf把整个文件读完后,再尝试读取%d时已经没有数据了,返回EOF,循环直接终止,所以i只增加到1,也就是只加载了1个“城市”,而这个城市的number_of_citizen是未初始化的垃圾值(你输出里的544366964就是这么来的)。
另外,城市名可能包含空格(比如Banja Luka),就算你用普通的%s读取也不行,因为%s会在空格处停止,只能读到Banja,漏掉Luka。
修复方案
我们需要正确处理带空格的城市名,最好的方法是先读取整行内容,再拆分城市名和数字。下面是修复后的完整代码:
#include <stdio.h> #include <string.h> struct City { char name[31]; int number_of_citizen; }; int load(struct City cities[100], int n) { FILE *fp = fopen("cities.txt", "r"); if (fp == NULL) { // 增加文件打开失败的检查 perror("Failed to open file"); return 0; } int i = 0; char line[100]; // 临时存储每行的完整内容 while (fgets(line, sizeof(line), fp) != NULL && i < n) { // 找到每行最后一个空格的位置,用来拆分城市名和数字 char *space_ptr = strrchr(line, ' '); if (space_ptr != NULL) { *space_ptr = '\0'; // 把空格换成字符串结束符,得到完整城市名 // 安全复制城市名到结构体,避免缓冲区溢出 strncpy(cities[i].name, line, sizeof(cities[i].name)-1); cities[i].name[sizeof(cities[i].name)-1] = '\0'; // 读取空格后面的数字 sscanf(space_ptr + 1, "%d", &cities[i].number_of_citizen); i++; } } fclose(fp); return i; } int main() { int i, number_of_cities; struct City cities[10]; FILE* fp = fopen("cities.txt", "w"); if (fp == NULL) { // 增加文件创建失败的检查 perror("Failed to create file"); return 1; } fputs("Sarajevo 345123", fp); fputc('\n', fp); // 用'\n'代替10,代码更易读 fputs("Tuzla 123456", fp); fputc('\n', fp); fputs("Mostar 101010", fp); fputc('\n', fp); fputs("Banja Luka 234987", fp); fclose(fp); number_of_cities = load(cities, 10); printf("%d\n", number_of_cities); for (i = 0; i < number_of_cities; i++) printf("%s,%d\n", cities[i].name, cities[i].number_of_citizen); return 0; }
关键修复点说明
- 用
fgets读取整行:确保能获取每行的完整内容,包括带空格的城市名。 - 用
strrchr拆分字符串:找到每行最后一个空格,把城市名和数字分开——不管城市名有多少个空格,都能正确提取。 - 增加文件检查:避免文件打开/创建失败时出现崩溃或未定义行为。
- 安全处理字符串:用
strncpy和手动添加结束符,防止城市名过长导致缓冲区溢出。
运行修复后的代码,就能得到你预期的输出结果了。
内容的提问来源于stack exchange,提问作者codproe
相关产品推荐
相关产品推荐

