C语言实现指定格式文件扫描及按序号检索数据的问题求解
C语言文件读取+序号查询功能实现方案
常见故障原因
之前两套读取逻辑出现全0、乱码问题,基本逃不过这几个原因:
- 未跳过首行表头:File.txt第一行是文本表头,不是有效数据,直接从文件起始位置按数值/字符串格式解析,会把表头文本写入数值变量,触发解析失败,后续文件指针位置错乱,所有数据读错
- 字符串读取未做长度限制:
name[20]最多存19个有效字符+1个字符串结束符,用无长度限制的%s读入时,一旦姓名长度超限会直接溢出数组,覆盖相邻的height、weight字段内存,直接导致数值乱码 - 未校验解析返回值:
fscanf/sscanf的返回值是成功匹配赋值的字段数,只要返回值不等于4(四个字段全匹配成功),就说明当前行解析失败,不能把无效值存入结构体,否则会残留脏数据 - 查询逻辑直接用输入序号当下标:序号不一定从0开始、也不一定连续,直接用
arr[输入序号]访问会出现数组越界,读到不属于数组的内存数据,表现为乱码甚至程序崩溃 - 未清理输入缓冲区:
scanf读入数字后会残留换行符在缓冲区,后续读入字符的逻辑会直接拿到这个换行符,导致交互逻辑异常,直接跳过用户输入。
参考实现
1. 约定File.txt格式
首行为表头,后续每行按「序号 姓名 身高(CM) 体重(KG)」顺序排列,字段间用空格/制表符分隔,示例内容:
序号 姓名 身高(CM) 体重(KG) 1 张三 175 65 2 李四 180 72 3 王五 168 52 4 赵六 183 78
2. 完整可运行代码
#include <stdio.h> #include <string.h> #define MAX_ENTRY 100 // 最多存储100条记录,可按需调整 #define NAME_LEN 20 // 数据存储结构体 typedef struct { int number; char name[NAME_LEN]; int height; float weight; } Person; /** * 从文件加载数据 * @param filePath 文件路径 * @param arr 存储数据的结构体数组 * @param count 传出参数,返回实际加载的记录条数 * @return 0加载成功,-1加载失败 */ int loadData(const char* filePath, Person arr[], int* count) { FILE* fp = fopen(filePath, "r"); if (!fp) { printf("打开文件失败,请检查文件路径是否正确\n"); return -1; } char lineBuf[1024]; *count = 0; // 读取并丢弃第一行表头 if (!fgets(lineBuf, sizeof(lineBuf), fp)) { fclose(fp); printf("文件内容为空\n"); return -1; } // 逐行读取解析数据 while (fgets(lineBuf, sizeof(lineBuf), fp) && *count < MAX_ENTRY) { // %19s限制姓名最大读入长度,防止数组溢出 int matchCnt = sscanf(lineBuf, "%d %19s %d %f", &arr[*count].number, arr[*count].name, &arr[*count].height, &arr[*count].weight ); // 只有四个字段全匹配成功才存入数组,计数+1 if (matchCnt == 4) { (*count)++; } } fclose(fp); return 0; } // 交互查询逻辑 void queryLoop(Person arr[], int total) { int queryNum; char continueFlag; while (1) { printf("\n请输入要查询的序号:"); // 校验输入是否为整数,非法输入清空缓冲区避免死循环 if (scanf("%d", &queryNum) != 1) { while (getchar() != '\n'); printf("输入无效,请输入数字类型的序号\n"); continue; } // 遍历匹配序号,不要直接用序号当数组下标 int hitIdx = -1; for (int i = 0; i < total; i++) { if (arr[i].number == queryNum) { hitIdx = i; break; } } if (hitIdx == -1) { printf("未查询到序号为%d的记录\n", queryNum); } else { printf("===== 查询结果 =====\n"); printf("序号:%d\n姓名:%s\n身高:%dCM\n体重:%.1fKG\n", arr[hitIdx].number, arr[hitIdx].name, arr[hitIdx].height, arr[hitIdx].weight ); } // 清空缓冲区残留的换行符,避免读入y/n时直接拿到换行 while (getchar() != '\n'); printf("是否继续查询?(y/n):"); scanf("%c", &continueFlag); if (continueFlag != 'y' && continueFlag != 'Y') { printf("已退出查询\n"); break; } } } int main() { Person people[MAX_ENTRY]; int dataTotal = 0; if (loadData("File.txt", people, &dataTotal) != 0) { return 1; } printf("数据加载完成,共读取%d条有效记录\n", dataTotal); queryLoop(people, dataTotal); return 0; }
关键注意点
- 优先选
fgets+sscanf的方案,比直接用fscanf鲁棒性更强,不容易因为格式不匹配导致文件指针错乱 - 所有IO操作都要做返回值校验,不要默认读文件、解析格式一定成功
- 字符串读入必须加长度限制,避免缓冲区溢出
- 交互场景下注意清理输入缓冲区,否则残留的换行、非法字符会导致后续输入逻辑异常
内容的提问来源于stack exchange,提问作者LearnerC
相关产品推荐
相关产品推荐

