C语言结构体写入文件后读取崩溃问题求助及代码修正
C语言读取逗号分隔文件还原结构体时fscanf崩溃的解决方法
问题描述
我编写的C语言代码可以正常将user结构体按指定格式写入文件,但读取文件还原结构体时,程序执行到fscanf就崩溃。怀疑读取用的格式字符串有问题,VSCode中%[^,]被拆分成两部分高亮,不确定是否合法。核心需求是实现C语言读取逗号分隔文件并赋值给结构体。相关代码如下:
const char *USER_INFO_OUT = "(%s, %s, %s, %s, %c, %s, %s, %s, %.3lf, %.3lf, %.3lf)"; const char *USER_INFO_IN = "(%[^,], %[^,], %[^,], %[^,], %c, %[^,], %[^,], %[^,], %.3lf, %.3lf, %.3lf)"; typedef struct user { char TCID[100]; char fullname[100]; char email[100]; char phone[100]; char gender; char address[100]; char password[100]; char customerID[100]; double balance; double savings; double debt; } user; user usr1; void signup() { FILE *fp; system("cls"); printf("\n\t\t\t--------------REGISTER A NEW ACCOUNT--------------\n\n\n"); printf("\n\tEnter Your Full Name:\t"); fgetc(stdin); takeinput(usr1.fullname); printf("\n\tEnter Your Tc. ID NO:\t"); takeinput(usr1.TCID); printf("\n\tEnter Your e-mail:\t"); takeinput(usr1.email); printf("\n\tEnter Your Phone No:\t"); takeinput(usr1.phone); printf("\n\tEnter Your Gender M/F:\t"); scanf("%c", &usr1.gender); fgetc(stdin); printf("\n\tEnter Your address:\t"); takeinput(usr1.address); printf("\n\tEnter Your password:\t"); takepassword(usr1.password); printf("\n\tEnter Your password again:\t"); takepassword(password2); // 注:password2需提前定义 if (!strcmp(usr1.password, password2)) { customerid(usr1.customerID); system("cls"); char folder[100] = "users\\"; strcat(folder, usr1.customerID); strcat(folder, ".dat"); fp = fopen(folder, "a+"); fprintf(fp, USER_INFO_OUT, usr1.fullname, usr1.TCID, usr1.email, usr1.phone, usr1.gender, usr1.address, usr1.password, usr1.customerID, usr1.balance, usr1.savings, usr1.debt); printf("\n\t\t\t--------------REGISTERATION COMPLETED--------------\n\n\n"); PlaySound(TEXT("Sounds\\signupdone.wav"), NULL, SND_ASYNC); printf("\n\tYou succesfully created an account %s", usr1.fullname); printf("\n\tThis is your Customer ID:\t%s", usr1.customerID); printf("\n\nSave Your Customer ID to Log in to Your account later!"); fclose(fp); } else { PlaySound(TEXT("Sounds\\invpass.wav"), NULL, SND_ASYNC); printf("\n\n\t Redirecting to main menu..."); Sleep(4000); registar(); } } void login() { FILE *f; system("cls"); printf("\n\t\t\tWELLCOME BACK TO AVCI BANK\n\n"); printf("\n\t\tPlease enter Your Customer ID: \t"); fgetc(stdin); takeinput(usr1.customerID); char folder[100] = "users\\"; strcat(folder, usr1.customerID); strcat(folder, ".dat"); f = fopen(folder, "r"); if (f == NULL) { printf("\n\n\tSorry This Account Doesn't Exist.\n\t Contact us for further information or Create a new account"); system("pause"); registar(); } else { fseek(f, 0, SEEK_SET); // 冗余,打开文件后指针默认在开头 fscanf(f, USER_INFO_IN, usr1.fullname, usr1.TCID, usr1.email, usr1.phone, &usr1.gender, usr1.address, usr1.password, usr1.customerID, &usr1.balance, &usr1.savings, &usr1.debt); system("cls"); printf("%s\t%s\t%s\t%s\t", usr1.fullname, usr1.TCID, usr1.email, usr1.address); system("pause"); } }
错误原因及修正方案
1. 读取格式字符串的核心错误
%.3lf不适合fscanf:fscanf读取浮点数时不需要指定精度,%.3lf是printf系列函数的输出格式,读取时只需用%lf即可。%c读取到空白字符:写入文件时,gender字段前后是,(逗号加空格),直接用%c会读取逗号后的空格,导致gender值错误,还会破坏后续字段的匹配逻辑。- 未限制
%[^,]的读取长度:%[^,]会一直读取到逗号,若内容超过结构体数组的长度(100),会导致缓冲区溢出,直接引发崩溃。 - VSCode的语法高亮是误判,
%[^,]是C标准支持的格式说明符,无需在意。
2. 修正后的代码
修正格式字符串
const char *USER_INFO_OUT = "(%s, %s, %s, %s, %c, %s, %s, %s, %.3lf, %.3lf, %.3lf)"; // 修正读取格式:添加数组长度限制、去掉浮点数精度、为%c前加空格跳过空白 const char *USER_INFO_IN = "(%99[^,], %99[^,], %99[^,], %99[^,], %c, %99[^,], %99[^,], %99[^,], %lf, %lf, %lf)";
%99[^,]:限制最多读取99个字符,留一个位置给字符串终止符'\0',避免缓冲区溢出。%c前的空格:让fscanf自动跳过逗号后的空白字符,正确读取M/F。
完善读取逻辑(可选但推荐)
在login函数中添加fscanf返回值检查,判断是否成功读取所有字段:
void login() { FILE *f; system("cls"); printf("\n\t\t\tWELLCOME BACK TO AVCI BANK\n\n"); printf("\n\t\tPlease enter Your Customer ID: \t"); fgetc(stdin); takeinput(usr1.customerID); char folder[100] = "users\\"; strcat(folder, usr1.customerID); strcat(folder, ".dat"); f = fopen(folder, "r"); if (f == NULL) { printf("\n\n\tSorry This Account Doesn't Exist.\n\t Contact us for further information or Create a new account"); system("pause"); registar(); } else { // 检查fscanf返回值,确认是否读取了11个字段 int read_count = fscanf(f, USER_INFO_IN, usr1.fullname, usr1.TCID, usr1.email, usr1.phone, &usr1.gender, usr1.address, usr1.password, usr1.customerID, &usr1.balance, &usr1.savings, &usr1.debt); if (read_count != 11) { printf("\n\n\t读取用户信息失败,请检查文件格式!"); system("pause"); fclose(f); registar(); } system("cls"); printf("%s\t%s\t%s\t%s\t%c\t%.3lf\n", usr1.fullname, usr1.TCID, usr1.email, usr1.address, usr1.gender, usr1.balance); system("pause"); fclose(f); } }
3. 其他注意事项
signup函数中的password2需提前声明(如全局变量或函数内局部变量),否则会编译错误。- 确保
takeinput、takepassword、customerid、registar等自定义函数逻辑正确,避免输入时残留换行符影响后续读取。
内容的提问来源于stack exchange,提问作者taymond
相关产品推荐
相关产品推荐

