使用fscanf读取银行客户文件至结构体数组仅首个数据生效,求解决
解决银行客户数据文件读取失败的问题
问题根源分析
你的代码存在多个关键错误,导致仅能读取首个客户数据:
- 错误的循环条件:
while(!feof(fptr))是典型误区,feof仅在读取操作尝试越过文件末尾后才会返回true,会导致循环多执行一次且无法正确识别读取失败。 - 取地址符遗漏:读取
balance时,fscanf("%f", customer[acc_count].balance)缺少&,%f要求传入float指针,直接传变量会触发内存访问错误。 - 数组索引错误:读取贷款金额时使用
loan[acc_count],acc_count是客户计数器,会超出loan数组的索引范围(0-2),应改为loan[1]。 - 贷款原因读取逻辑错误:
fscanf("%s")仅能读取单个空格分隔的字符串,无法读取整行贷款原因;且参数传递错误,%s不能同时对应三个字符串参数。此外,前序fscanf会在缓冲区留下换行符,导致后续fgets读取空行。 - 未处理客户间空行:文件中每个客户数据后存在空行,原代码未处理,导致下一次读取姓名时读到空行,后续数据全部错位。
- 空指针非法操作:读模式打开失败时,
fptr为NULL,此时调用fclose(fptr)属于未定义行为。
修正后的代码
#include <stdio.h> #include <string.h> #define MAX_ACCOUNTS 100 // 根据实际需求调整最大值 struct bankInfo{ char name[50]; int pin; float balance; int loan[3]; int loan_Total; int loan_count; char loan_reason[3][1000]; float creditScore; }customer[MAX_ACCOUNTS], closed_customers[MAX_ACCOUNTS]; int main() { FILE *fptr; char str[100]; int acc_count = 0; char dummy_buf[100]; // 用于清理缓冲区换行符和空行 fptr = fopen("BankManagement.txt", "r"); if(fptr == NULL){ // 读模式失败则创建新文件 fptr = fopen("BankManagement.txt", "w"); if(fptr == NULL){ printf("无法创建文件\n"); return 1; } fclose(fptr); return 0; } // 以读取姓名作为循环终止条件,直接判断读取是否成功 while(fgets(str, 100, fptr) != NULL){ // 跳过客户间的空行 if(strlen(str) <= 1){ continue; } // 去除换行符 str[strcspn(str, "\n")] = '\0'; strcpy(customer[acc_count].name, str); // 读取PIN,校验读取结果 if(fscanf(fptr, "%d", &customer[acc_count].pin) != 1) break; // 读取余额,修复取地址符问题 if(fscanf(fptr, "%f", &customer[acc_count].balance) != 1) break; // 读取3项贷款金额,修正索引错误 if(fscanf(fptr, "%d %d %d", &customer[acc_count].loan[0], &customer[acc_count].loan[1], &customer[acc_count].loan[2]) != 3) break; // 读取贷款次数 if(fscanf(fptr, "%d", &customer[acc_count].loan_count) != 1) break; // 清理fscanf留下的换行符,避免干扰后续fgets fgets(dummy_buf, 100, fptr); // 读取整行贷款原因 if(fgets(str, 100, fptr) == NULL) break; str[strcspn(str, "\n")] = '\0'; // 拆分贷款原因到数组 char *token = strtok(str, " "); int i = 0; while(token != NULL && i < 3){ strcpy(customer[acc_count].loan_reason[i], token); token = strtok(NULL, " "); i++; } // 无贷款时填充"0" while(i < 3){ strcpy(customer[acc_count].loan_reason[i], "0"); i++; } // 读取贷款总额 if(fscanf(fptr, "%d", &customer[acc_count].loan_Total) != 1) break; // 读取信用分 if(fscanf(fptr, "%f", &customer[acc_count].creditScore) != 1) break; // 清理当前客户数据后的换行符,准备读取下一个客户 fgets(dummy_buf, 100, fptr); acc_count++; // 防止数组越界 if(acc_count >= MAX_ACCOUNTS){ printf("客户数量已达上限\n"); break; } } fclose(fptr); // 测试输出读取结果 for(int i=0; i<acc_count; i++){ printf("客户%d:\n", i+1); printf("姓名:%s\n", customer[i].name); printf("PIN:%d\n", customer[i].pin); printf("余额:%.1f\n", customer[i].balance); printf("贷款金额:%d %d %d\n", customer[i].loan[0], customer[i].loan[1], customer[i].loan[2]); printf("贷款次数:%d\n", customer[i].loan_count); printf("贷款原因:%s %s %s\n", customer[i].loan_reason[0], customer[i].loan_reason[1], customer[i].loan_reason[2]); printf("贷款总额:%d\n", customer[i].loan_Total); printf("信用分:%.1f\n\n", customer[i].creditScore); } return 0; }
核心修正点说明
- 用
while(fgets(str, 100, fptr) != NULL)作为循环条件,直接判断姓名读取是否成功,同时跳过空行。 - 修复了
balance读取时的取地址符问题,以及贷款金额数组的索引错误。 - 改用
fgets读取整行贷款原因,再用strtok拆分到对应数组元素,处理无贷款时的填充逻辑。 - 每次
fscanf后清理缓冲区换行符,避免干扰后续读取操作。 - 添加数组越界检查,防止超出
MAX_ACCOUNTS限制。 - 移除空指针的
fclose调用,添加文件创建失败的错误处理。
内容的提问来源于stack exchange,提问作者Kookaburra
相关产品推荐
相关产品推荐

