问题及问题代码:
将前面的第四版银行储蓄系统(第四版)开发,中用数组存储用户信息,改为使用结构体来存储
实例代码:
总览:
main.c
#include <stdio.h>
#include "bank.h"
/*主函数*/
int main()
{
printf("+-------------------------------------+\n");
printf("+ 欢迎光临乔氏银行 +\n");
printf("+-------------------------------------+\n");
if(pass())
{
readDate(); //读入数据
work(); //业务驱动
writeDate(); //写入数据
}
return 0;
}
bank.h
#ifndef BANK_H_INCLUDED
#define BANK_H_INCLUDED
#define upNum 2000 //系统可容纳最大客户数量
struct data{
int account; //账号
int Password; //密码
char name[10]; //用户名
double balance; //账户余额
int status; //状态
};
struct data user[upNum];
int N; //实际用户数量
int pass(); //业务员帐号登录
void readDate(); //读入数据
void writeDate(); //写入数据
void work(); //业务驱动
int chooseInMenu(); //选择菜单,并由业务员进行选择
void showAccount(); //查询余额
void openAccount(); //开户
void cancelAccount(); //销户
void reportLoss(); //挂失
void cancelLoss(); //解挂
void save(); //存款
void withdraw(); //取款
void transferAccounts(); //转账
void updatePasswork(); //改密码
int inputPassword(); //输入密码并做加密处理,同时返回密码值
int search(int); //根据帐号找到用户数据所对应的下标
//以上为全局变量以及函数的声明
#endif // BANK_H_INCLUDED
bank.c
#include<stdio.h>
#include<ctype.h>
#include "bank.h"
/*
功能:开户
说明:在进入系统时,读入数据的过程中,已经记录了实际用户数量为N,在数组中的范围0~N-1
开户是要增加一个用户,并向数组下标为N的元素赋值,成功后同时N++
但是顺序增加不方便后续的用户查找功能(只能使用顺序查找,这是不合算的)
因此:开户时,根据帐号,将数据插入到数组的相应位置,使帐号有序排列,
这样在search查找用户函数中就可以使用二分查找算法,大大加快查找速度
*/
void openAccount()
{
int i, j;
struct data temp;
if(upNum == N)
{
printf("银行用户数已达到上限,不能完成开户!\n");
return;
}
printf("正在开户\n");
printf("账号:");
scanf("%d",&user[N].account);
printf("户主姓名:");
scanf("%s",&user[N].name);
int iPass1,iPass2;
printf("密码:");
iPass1 = inputPassword();
printf("确认密码:");
iPass2 = inputPassword();
if(iPass1 == iPass2)
{
user[N].Password = iPass1;
user[N].status = 0; //账号状态正常
printf("存入金额:");
scanf("%lf",&user[N].balance);
N++; //正式用户增加1
printf("开户成功!\n");
for(i = 0 ; i < N - 1 ; i++)
{
for(j = 0 ; j < N-i-1 ; j++)
{
if(user[j].account > user[j+1].account)
{
temp = user[j];
user[j] = user[j+1];
user[j+1] = user[j];
}
}
}
}
else
{
printf("两次密码不一致,开户失败!\n");
}
return;
}
/*
功能:注销账户
说明:找到账户信息,将状态改为2-注销即可
注销前应注意账户余额,如有存款,应先取款在注销
*/
void cancelAccount()
{
int id; //用于输入账号
int who; //查找该账号所对应数组下标
int iPass;
printf("待销户账号:");
scanf("%d",&id);
who = search(id); //根据账号查询用户,返回用户下标
if(who<0) //说明id用户不存在
{
printf("该用户不存在,销户失败!\n");
}
else
{
printf("户主姓名:%s\n",user[who].name);
printf("密码:");
iPass = inputPassword();
if(iPass != user[who].Password)
{
printf("密码输入错误,不能继续操作!\n");
}
else
{
printf("账户余额:%.2lf",user[who].balance);
printf("确认销户(Y/N):");
if(tolower(getchar())=='y')
{
user[who].balance = 0; //销户后余额变为0
user[who].status = 2; //状态变为销户
printf("取款成功,余款%.2lf元.\n",user[who].balance);
printf("销户成功!\n");
}
else
{
printf("您取消操作,销户失败!\n");
}
}
}
return;
}
/*
功能:存款
说明:存款账户必须存在,同时账户需处在正常状态方能存款
*/
void save()
{
int id,who;
double money;
printf("请输入存款账号:");
scanf("%d",&id);
who = search(id);
if(who < 0)
{
printf("该用户不存在,存款失败!\n");
}
else
{
if(user[who].status==0)
{
do
{
printf("户主姓名:%s\n",user[who].name);
printf("请输入存款金额:");
scanf("%lf",&money);
user[who].balance += money;
printf("存款成功!\n是否继续存款(Y/N):");
fflush(stdin);
}
while(tolower(getchar())=='y');
}
else if(user[who].status == 1)
{
printf("该用户处于挂失状态,存款失败!\n");
}
else
{
printf("该用户已经销户,存款失败!\n");
}
}
return;
}
/*
功能:取款
说明:取款账户必须存在,同时账户需处在正常状态方能取款
账户余额应大于取款金额,否则不能取款
*/
void withdraw()
{
int id,who,iPass;
double money;
printf("请输入取款账号:");
scanf("%d",&id);
who = search(id);
if(who<0)
{
printf("该用户不存在,取款失败!\n");
}
else
{
if(user[who].status == 0)
{
printf("户主姓名:%s\n",user[who].name);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who].Password)
{
printf("密码错误,取款失败!\n");
}
else
{
do
{
printf("请输入取款金额:");
scanf("%lf",&money);
if(money>user[who].balance)
{
printf("余额不足,取款失败!\n");
}
else
{
user[who].balance -= money;
printf("取款成功!账户余额为:%.2lf\n",user[who].balance);
printf("是否继续取款(Y/N):");
fflush(stdin);
}
}
while(tolower(getchar())=='y');
}
}
else if(user[who].status==1)
{
printf("该用户处于挂失阶段,取款失败!\n");
}
else
{
printf("该用户已注销,取款失败!\n");
}
}
return;
}
/*
功能:查询余额
说明:显示账户信息
*/
void showAccount()
{
int id,who,iPass;
printf("请输入待查询的账号:");
scanf("%d",&id);
who = search(id);
if(who<0)
{
printf("该用户不存在,查询失败!\n");
}
else
{
printf("户主姓名:%s\n",user[who].name);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who].Password)
{
printf("密码错误,查询失败!\n");
}
else
{
printf("账户余额为:%.2lf\n",user[who].balance);
printf("状态:");
if(user[who].status==0)
{
printf("正常\n");
}
else if(user[who].status==1)
{
printf("挂失\n");
}
else
{
printf("已经销户\n");
}
}
}
return;
}
/*
功能:挂失账户
说明:需要验证用户身份信息
*/
void reportLoss()
{
int id,who,iPass;
printf("请输入需要挂失的账号:");
scanf("%d",&id);
who = search(id);
if(who<0)
{
printf("该账户不存在,挂失失败!\n");
}
else
{
printf("户主姓名:%s\n",user[who].name);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who].Password)
{
printf("密码错误,挂失失败!\n");
}
else
{
if(user[who].status == 0)
{
user[who].status = 1; //挂失状态
printf("挂失成功!\n");
}
else if(user[who].status==1)
{
printf("该账户已处于挂失状态!\n");
}
else if(user[who].status==2)
{
printf("该账户已注销,不能挂失!\n");
}
}
}
return;
}
/*
功能:解除挂失
说明:帐号要处于挂失状态,同时需要验证身份信息
*/
void cancelLoss()
{
int id,who,iPass;
printf("请输入待解挂的账号:");
scanf("%d",&id);
who = search(id);
if(who<0)
{
printf("该账户不存在,解挂失败!\n");
}
else
{
printf("户主姓名:%s\n",user[who].name);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who].Password)
{
printf("密码错误,解挂失败!\n");
}
else
{
if(user[who].status==0)
{
printf("该账号处于正常状态,无需解挂!\n");
}
else if(user[who].status==1)
{
user[who].status = 0;
printf("解除挂失成功!\n");
}
else if(user[who].status==2)
{
printf("该账号已销户,解挂失败!\n");
}
}
}
return;
}
/*
功能:转账
说明:要保证两个用户都存在并处于正常状态,并且转出账户余额应大于转出额,才能转账
*/
void transferAccounts()
{
int id,whoout,whoin;
int iPass;
double money;
printf("请输入转出账号:");
scanf("%d",&id);
whoout = search(id);
if(whoout < 0)
{
printf("该用户不存在,转账失败!\n");
}
else
{
if(user[whoout].status==0)
{
printf("转出户主姓名:%s",user[whoout].name);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[whoout].Password)
{
printf("密码错误,转账失败!\n");
}
else
{
printf("输入转账金额:");
scanf("%lf",&money);
if(money>user[whoout].balance)
{
printf("余额不足,转账失败!\n");
}
else
{
printf("输入转入账号:");
scanf("%d",&id);
whoin = search(id);
if(whoin<0)
{
printf("转入账号不存在,转账失败!\n");
}
else
{
if(user[whoin].status>0)
{
printf("转入账号异常,转账失败!\n");
}
else
{
printf("转入户主姓名:%s\n",user[whoin].name);
user[whoout].balance -= money;
user[whoin].balance += money;
printf("转账后您账户余额为:%.2lf\n",user[whoout].balance);
}
}
}
}
}
else
{
printf("该账户异常,转账失败!\n");
}
}
return;
}
/*
功能:修改密码
*/
void updatePasswork()
{
int id,who;
int iPass,newPass,rePass;
printf("请输入账号:");
scanf("%d",&id);
who = search(id);
if(who<0)
{
printf("该账号不存在,修改密码失败!\n");
}
else
{
printf("户主姓名:%s",user[who].name);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who].Password)
{
printf("密码错误,不能继续操作!\n");
}
else
{
printf("新密码:");
newPass = inputPassword();
printf("确认密码:");
rePass = inputPassword();
if(newPass == rePass)
{
user[who].Password = newPass;
printf("修改成功!\n");
}
else
{
printf("两次输入不同,修改失败!\n");
}
}
}
return;
}
/*
功能:根据帐号查询用户,并返回用户下标
入口参数:需要查询用户的帐号
返回值:如果该用户存在,返回该用户在数组中的下标,否则返回负数(-1)
说明:由于前面开户模块已将用户按顺序排列,所以该模块可以使用二分查找而加快查找速度
*/
int search(int id)
{
int index = -1;
int low = 0, high = N-1;
int mid;
while(low<=high)
{
mid = (low + high)/2;
if(id == user[mid].account)
{
index = mid;
break;
}
else if(id<user[mid].account)
{
high = mid-1;
}
else
{
low = mid+1;
}
}
return index;
}
work.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<conio.h>
#include<ctype.h>
#include "bank.h"
/*
功能:验证业务员密码
返回值:密码正确返回1
不能通过密码验证,即密码错误返回0
*/
/*
关于getch()的一点说明:
所在头文件:conio.h
函数用途:从控制台读取一个字符,但不显示在屏幕上
函数原型:int getch(void)
返回值:读取的字符
在不同平台,输入回车,getch()将返回不同数值,而getchar()统一返回10(即\n)
1)windows平台下ENTER键会产生两个转义字符 \r\n,因此getch返回13(\r)。
2)unix、 linux系统中ENTER键只产生 \n ,因此getch返回10(\n)。
3)MAC OS中ENTER键将产生 \r ,因此getch返回13(\r)。
为避免键盘缓存区中未读出的字符影响程序,用fflush(stdin);清除输入缓存区
*/
int pass()
{
char nameInfile[20]; //读取文件中的用户名
char passwordInfile[20]; //读取相应用户的密码
char name[20]; //从键盘输入的用户名
char password[20]; //从键盘输入的密码
char ch;
int right = 0; //返回验证信息,用户名密码正确,返回1,否则返回0
int iTry = 3; //可验证次数
FILE *fp;
//从密码文件中取出数据
if((fp = fopen("worker.txt","r")) == NULL)
{
printf("password file open error!");
exit(0);
}
fscanf(fp, "%s %s", nameInfile,passwordInfile);
fclose(fp);
//进入系统,密码三次不对将退出
do
{
printf("请输入业务员用户名:");
scanf("%s",name);
printf("请输入密码:");
int i = 0;
while((ch = getch())!='\r') //getch接受输入后不显示
{
password[i++] = ch;
putchar('*'); //接受任何字符,屏幕上只显示*
}
password[i] = '\0';
fflush(stdin);
printf("\n");
if(strcmp(name,nameInfile)==0&&strcmp(password,passwordInfile)==0)
{
right = 1;
break;
}
else
{
iTry--;
if(iTry > 0)
{
printf("超过三次将退出,您还可以尝试%d次\n",iTry);
}
else
{
printf("对不起,您不能进入系统!\n");
}
}
}
while(iTry > 0);
return right;
}
/*
功能:办理业务
*/
void work()
{
int ichoose;
do
{
ichoose = chooseInMenu();
switch(ichoose)
{
case 1:
openAccount();
break;
case 2:
cancelAccount();
break;
case 3:
save();
break;
case 4:
withdraw();
break;
case 5:
showAccount();
break;
case 6:
transferAccounts();
break;
case 7:
reportLoss();
break;
case 8:
cancelLoss();
break;
case 9:
updatePasswork();
break;
case 0:
printf("欢迎您下次光临!\n");
}
}
while(ichoose);
return;
}
/*
功能:从文件中读取储户数据
*/
/*
功能:从文件中读入用户数据
*/
void readDate()
{
FILE *fp; //用于文件操作
int i = 0;
if((fp = fopen("userData.dat","r"))==NULL) //这里使用dat文件保存数据
{
printf("user date file open error!\n");
exit(0);
}
while(fscanf(fp,"%d %s %d %lf %d",&user[i].account,&user[i].name,&user[i].Password,&user[i].balance,&user[i].status) != EOF)
{
i++;
}
N = i; //变量N保存实际用户数量
fclose(fp);
return;
}
/*
功能:将用户数据保存到文件中
*/
void writeDate()
{
FILE *fp;
int i;
if((fp = fopen("userData.dat","w"))==NULL)
{
printf("user data file open error!\n");
exit(0);
}
for(i = 0 ; i < N ; i++)
{
fprintf(fp,"%d %s %d %lf %d\n",user[i].account,user[i].name,user[i].Password,user[i].balance,user[i].status);
}
fclose(fp);
return;
}
/*
功能:显示菜单并由业务员选择
返回值:用户选择的功能:范围0-9之间
*/
int chooseInMenu()
{
int Menu;
while(1) //选择错误可以重新选择
{
printf("+------------------------------------+\n");
printf("+ 1.开户 2.销户 3.存款 +\n");
printf("+ 4.取款 5.查询余额 6.转账 +\n");
printf("+ 7.挂失 8.解挂 9.修改密码 +\n");
printf("+ 0.退出系统 +\n");
printf("+------------------------------------+\n");
printf("请输入操作指令:");
scanf("%d",&Menu);
if(Menu>=0&&Menu<=9) //保证选择的指令符合
{
break;
}
else
{
printf("请重新选择功能!\n");
}
}
return Menu;
}
/*
功能:输入密码
返回值:整形密码值
说明:1.该函数在每一模块均要使用,同时功能单一,故分离出来单独做一个函数来实现
2.为方便进行显示加密处理,故输入时以字符的形式输入,后转换为对应整数并返回
3.规定密码由不全为0的6位数字构成(当开头是'0'时,实际不足6位),一旦输入错误将重新输入
*/
int inputPassword()
{
char password; //接受字符形式密码
int iPass = 0; //转换后的数字密码
int i;
while(1)
{
for(i = 0 ; i < 6 ; i++)
{
password = getch();
putchar('*');
if(isdigit(password)) //isdigit检测传入的是否为十进制数
{
iPass = iPass*10+(password-'0'); //iPass
}
else
{
iPass = 0;
break;
}
}
fflush(stdin);
printf("\n");
if(iPass == 0) //两种情况,1.输入全为0正常退出 2.输入了非数字字符被重置为0
{
printf("密码要求为全数字,切不能全为0\n");
printf("请重新输入密码:");
}
else
{
break;
}
}
return iPass;
}
总结:
后期还会将用户储存改为使用链表的形式进行存储,但单链表储存在查找数据时,只能使用顺序查找,这样会导致效率低下,当做一次练习来做,更加熟悉链表的使用。