You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:23:10