C语言进阶-第39讲:银行储蓄系统(二进制版)开发

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_39286218/article/details/78759353

银行储蓄系统:结构体版本二进制版本

下面讨论二进制版较结构体版的不同,优势,两难,改进之处:


不同:

(一)全局变量

结构体版:

struct record users[upNum];   //银行所有用户
int N;                                        //实际的用户数目
二进制版:

int N;
FILE *fp;

全局变量的结构体数组->子函数中定义指向结构体的指针

fp由局部变量->全局变量


(二)main函数

结构体版:

int main()
{
    printf("+----------------------+\n");
    printf("+    欢迎光临CSDN银行  +\n");
    printf("+----------------------+\n");
    if (pass())
    {
        readData();
        work();
        writeData();
    }
    return 0;
}
二进制版:

int main()
    {
        FILE *fp;
        printf("+----------------------+\n");
        printf("+    欢迎光临我家银行  +\n");
        printf("+----------------------+\n");
        if (pass())
        {
            exchange();    //将数据从文本文件读到二进制文件
            if((fp=fopen("account.bin", "rb+"))==NULL)   //文件不存在时重建,文件已经存在时,将保留原数据
            {
                printf("数据文件打开失败,退出程序....");
                exit(1);
            }
            fseek(fp, 0, SEEK_END);
            N = ftell(fp)/sizeof(record);  //ftell用于得到文件位置指针当前位置相对于文件首的偏移字节数,除以sizeof(struct record),得到的是当前用户的数目
            work();
            fclose(fp);
        }
        return 0;
    }
结构体版readData(),writeData() 需要用到大数组操作,需要读写所有的数据信息;

二进制版运用随机读写,只要定位到某账户所在的文件指针位置,不再需要内存中的大数组。


(三)二进制的业务员文件及存储

结构体版:

int pass()
{
    ...
    if ((fp=fopen("worker.txt", "r"))==NULL)
    {
        printf("password file cannot open!");
        exit(0);
    }
    fscanf(fp, "%s %s", sNameInFile, sPassInFile);  //从文件中读业务员用户名和密码密码
    fclose(fp);
    //进入系统,密码三次不对将退出
    do
    {
        ...
    }
    while(iTry);
    return right;
}
二进制版:
int pass()
{
    ...
    if ((fp=fopen("password.dat", "rb"))==NULL)
    {
        printf("第一次使用,将设置初始操作员账户:\n");
        printf("请输入用户名:");
        scanf("%s",sName);
        printf("请输入密码:");
        iPass1=inputPassword();  //输入密码1
        printf("确认密码:");
        iPass2=inputPassword();  //输入密码2
        if(iPass1==iPass2)
        {
            fp=fopen("password.dat", "wb");
            fwrite(sName, sizeof(sName), 1, fp);
            fwrite((char*)&iPass1, sizeof(int), 1, fp);
            right = 1;
            fclose(fp);
       }
        else
        {
            printf("两次密码不一致,设置不成功!\n");
            exit(0);
        }
    }
    else
    {
        fread(sNameInFile, sizeof(sNameInFile), 1, fp);  //从文件中读业务员用户名
        fread((char*)&iPassInFile, sizeof(int), 1, fp);  //密码
        fclose(fp);
        //进入系统,密码三次不对将退出
        do
        {
            ...
        }
        while(iTry);
    }
    return right;
}
结构体版中用字符数组保存业务员密码;二进制版中密码采用整型,添加了打开失败的处理

(四)从文件中读取以及更新内容——以取款为例

结构体版:

void withdraw()
{
    ...
    printf("账号:");
    scanf("%d", &id);
    who = search(id);  //根据账号查询用户,返回用户的下标
    if(who<0)   //说明id账户不存在
    {
        printf("该用户不存在,取款失败!\n");
    }
    else
    {
        if(users[who].status==0)
        {
            printf("户主姓名:%s\n", users[who].name);
            printf("密码:");
            iPass=inputPassword();
            if(iPass!=users[who].password)
            {
                printf("输入密码错误,取款失败!\n");
            }
            else
            {
                printf("输入取款额:");
                scanf("%lf", &money);
                if(money>users[who].balance)  //亲,不玩透支
                {
                    printf("余额不足,取款失败!\n");
                }
                else
                {
                    users[who].balance-=money;
                    printf("取款后,还有%.2f元. \n",users[who].balance);
                }
            }
        }
        else if(users[who].status==1)
        ...
        else
        ...
    }
    return;
}

int search(int id)
{
    int index=-1;
    int i;
    for(i=0; i<N; i++)
    {
        if(users[i].account==id)
        {
            index=i;
            break;   //找到了,立即退出循环
        }
    }
    return index; //若找到,其值在0~N-1间,否则,保持-1
}
二进制版:

void withdraw()
{
    ...
    record *user;
    user = getuser();  //输入并查询用户,不存在返回NULL
    if(user!=NULL)
    {
        if(user->status==0)
        {
            printf("户主姓名:%s\n", user->name);
            printf("密码:");
            iPass=inputPassword();
            if(iPass!=user->password)
            {
                printf("输入密码错误,取款失败!\n");
            }
            else
            {
                printf("输入取款额:");
                scanf("%lf", &money);
                if(money>user->balance)  //亲,不玩透支
                {
                    printf("余额不足,取款失败!\n");
                }
                else
                {
                    user->balance-=money;
                    writeData(user);
                    printf("取款后,还有%.2f元. \n",user->balance);
                }
            }
        }
        else if(user->status==1)
        ...
        else
        ...
        free(user);
    }
    return;
}
record* getuser()
{
    FILE *fp;
    if((fp=fopen("account.bin", "rb+"))==NULL)   //文件不存在时重建,文件已经存在时,将保留原数据
    {
        printf("数据文件打开失败,退出程序....");
        exit(1);
    }
    int id;
    printf("账号:");
    scanf("%d", &id);
    record *user = NULL;
    if(id>=N+10001)//说明账户不存在
    {
        printf("该用户不存在,查询完毕!\n");
    }
    else
    {
        fseek(fp,(id-10001)*sizeof(record), SEEK_SET);
        user=(record*)malloc(sizeof(record));
        fread((char*)user, sizeof(record), 1, fp);
    }
    fclose(fp);
    return user; //若找不到,user为NULL
}
void writeData(record *user)
{
    FILE *fp;
    if((fp=fopen("account.bin", "rb+"))==NULL)   //文件不存在时重建,文件已经存在时,将保留原数据
    {
        printf("数据文件打开失败,退出程序....");
        exit(1);
    }
    //先在文件中定位
    long i = user->account -10001;
    fseek(fp, (long)i*sizeof(record), SEEK_SET);
    fwrite((char*)user, sizeof(record),1,fp);
    fclose(fp);
}
二进制版随机读取并采用动态分配内存的方式,避免大数组操作和节省空间

(五)创建二进制文件account.bin用来保存储户信息

#define upNum 2000           //估计一个超出原实际储户的大数
void exchange(){
    FILE *fp;
    record users[upNum];     //存放数据的一段内存空间
    int i=0,N;
    /*事先将储户信息输入到文本文件中*/
    if((fp=fopen("account.dat","r"))==NULL){   
        printf("account.dat cannot be opened");
        exit(1);
    }
    /*将文本文件中的信息读到内存*/
    while(fscanf(fp,"%d %s %d %lf %d",&users[i].account, users[i].name, &users[i].password, &users[i].balance,&users[i].status ) != EOF)
        i++;
    N=i;
    fclose(fp);
    if((fp=fopen("account.bin","wb"))==NULL){
        printf("account.bin cannot be opened");
        exit(1);
    }
    /*将内存数据读到二进制文件中*/
    fwrite((char*)&users[0],sizeof(users[0]),N,fp);
    fclose(fp);
}


优势:

二进制文件:空间省、效率高、保密。结合随机读取,自由灵活,适用大文件


两难:

fp作为全局变量,又是指针,在整个文件中起作用,生存周期长

若将fp作为局部变量(定义FILE *fp;fopen();fclose();),但会在writeData(),getuser()中频繁调用,使用外存,过程缓慢


改进之处:索引+随机访问

实际应用中数据文件未必顺序存储,但账号不会重复,
使用数据在内存中的偏移量代替序号,声明第几条记录
把偏移量和账号抽取出来作为排序前索引,根据账号查出是第几条记录
通过以账号进行排序后的索引文件,将索引数据读到内存,快速查找到账户信息
查找账号->偏移量->储户数据信息








猜你喜欢

转载自blog.csdn.net/sinat_39286218/article/details/78759353