这是提高篇的最后一个程序,将会用到前面所讲的很多知识,同时很复杂,我跟着贺老师的代码自己做了一遍,这就是提升的过程!!
问题及代码:
/*
Copyright(c)2018,CSDN学院行者-
All rights reserved.
文件名称:main.c
作者:乔帅
完成日期:2018年4月6日
版本号:v1.0
问题描述:小小型应用系统开发指导(四)- 银行储蓄系统(四)
输入描述:帐号、密码等
程序操作:根据选的功能部分进行操作
程序输出:根据选择的功能操作后,输出操作后的结果
*/
/*
这将会是一个漫长的过程,也将是一个学习的过程,做下来就会有收获,加油!
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<conio.h>
#include<ctype.h>
#define upNum 2000 //系统可容纳最大客户数量
int user[upNum][3]; //用与存放帐号、密码、帐号状态的二位数组
//用户状态0代表正常、1代表挂失、2代表销户
char name[upNum][10]; //用于存放用户名的二维数组
double balance[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); //根据帐号找到用户数据所对应的下标
//以上为全局变量以及函数的声明
/*主函数*/
int main()
{
printf("+-------------------------------------+\n");
printf("+ 欢迎光临乔氏银行 +\n");
printf("+-------------------------------------+\n");
if(pass())
{
readDate(); //读入数据
work(); //业务驱动
writeDate(); //写入数据
}
return 0;
}
/*
功能:验证业务员密码
返回值:密码正确返回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 readDate()
{
FILE *fp; //用于文件操作
int i = 0;
if((fp = fopen("userDate.dat","r"))==NULL) //这里使用dat文件保存数据
{
printf("user date file open error!\n");
exit(0);
}
while(fscanf(fp,"%d %s %d %lf %d",&user[i][0],&name[i],&user[i][1],&balance[i],&user[i][2]) != EOF)
{
i++;
}
N = i; //变量N保存实际用户数量
fclose(fp);
return;
}
/*
功能:将用户数据保存到文件中
*/
void writeDate()
{
FILE *fp;
int i;
if((fp = fopen("userDate.dat","w"))==NULL)
{
printf("user date file open error!\n");
exit(0);
}
for(i = 0 ; i < N ; i++)
{
fprintf(fp,"%d %s %d %lf %d\n",user[i][0],name[i],user[i][1],balance[i],user[i][2]);
}
fclose(fp);
return;
}
/*
功能:办理业务
*/
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;
}
/*
功能:显示菜单并由业务员选择
返回值:用户选择的功能:范围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;
}
/*
功能:开户
说明:在进入系统时,读入数据的过程中,已经记录了实际用户数量为N,在数组中的范围0~N-1
开户是要增加一个用户,并向数组下标为N的元素赋值,成功后同时N++
但是顺序增加不方便后续的用户查找功能(只能使用顺序查找,这是不合算的)
因此:开户时,根据帐号,将数据插入到数组的相应位置,使帐号有序排列,
这样在search查找用户函数中就可以使用二分查找算法,大大加快查找速度
*/
void openAccount()
{
int i, j;
int temp;
char ch;
double d;
if(upNum == N)
{
printf("银行用户数已达到上限,不能完成开户!\n");
return;
}
printf("正在开户\n");
printf("账号:");
scanf("%d",&user[N][0]);
printf("户主姓名:");
scanf("%s",&name[N]);
int iPass1,iPass2;
printf("密码:");
iPass1 = inputPassword();
printf("确认密码:");
iPass2 = inputPassword();
if(iPass1 == iPass2)
{
user[N][1] = iPass1;
user[N][2] = 0; //账号状态正常
printf("存入金额:");
scanf("%lf",&balance[N]);
N++; //正式用户增加1
printf("开户成功!\n");
for(i = 0 ; i < N - 1 ; i++)
{
for(j = 0 ; j < N-i-1 ; j++)
{
if(user[j][0] > user[j+1][0])
{
temp = user[j][0]; //账号互换
user[j][0] = user[j+1][0];
user[j+1][0] = temp;
temp = user[j][1]; //密码互换
user[j][1] = user[j+1][1];
user[j+1][1] = temp;
temp = user[j][2]; //状态互换
user[j][2] = user[j+1][2];
user[j+1][2] = temp;
ch = name[j][10]; //用户名互换
name[j][10] = name[j+1][10];
name[j+1][10] = ch;
d = balance[j]; //余额互换
balance[j] = balance[j+1];
balance[j+1] = d;
}
}
}
}
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",name[who]);
printf("密码:");
iPass = inputPassword();
if(iPass != user[who][1])
{
printf("密码输入错误,不能继续操作!\n");
}
else
{
printf("账户余额:%.2lf",balance[who]);
printf("确认销户(Y/N):");
if(tolower(getchar())=='y')
{
balance[who] = 0; //销户后余额变为0
user[who][2] = 2; //状态变为销户
printf("取款%.2lf元,销户成功!\n",balance[who]);
}
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][2]==0)
{
do
{
printf("户主姓名:%s\n",name[who]);
printf("请输入存款金额:");
scanf("%lf",&money);
balance[who] += money;
printf("存款成功!\n是否继续存款(Y/N):");
fflush(stdin);
}
while(tolower(getchar())=='y');
}
else if(user[who][2] == 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][2] == 0)
{
printf("户主姓名:%s\n",name[who]);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who][1])
{
printf("密码错误,取款失败!\n");
}
else
{
do
{
printf("请输入取款金额:");
scanf("%lf",&money);
if(money>balance[who])
{
printf("余额不足,取款失败!\n");
}
else
{
balance[who] -= money;
printf("取款成功!账户余额为:%.2lf\n",balance[who]);
printf("是否继续取款(Y/N):");
fflush(stdin);
}
}
while(tolower(getchar())=='y');
}
}
else if(user[who][2]==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",name[who]);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who][1])
{
printf("密码错误,查询失败!\n");
}
else
{
printf("账户余额为:%.2lf\n",balance[who]);
printf("状态:");
if(user[who][2]==0)
{
printf("正常\n");
}
else if(user[who][2]==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",name[who]);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who][1])
{
printf("密码错误,挂失失败!\n");
}
else
{
if(user[who][2] == 0)
{
user[who][2] = 1; //挂失状态
printf("挂失成功!\n");
}
else if(user[who][2]==1)
{
printf("该账户已处于挂失状态!\n");
}
else if(user[who][2]==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",name[who]);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who][1])
{
printf("密码错误,解挂失败!\n");
}
else
{
if(user[who][2]==0)
{
printf("该账号处于正常状态,无需解挂!\n");
}
else if(user[who][2]==1)
{
user[who][2] = 0;
printf("解除挂失成功!\n");
}
else if(user[who][2]==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][2]==0)
{
printf("转出户主姓名:%s",name[whoout]);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[whoout][1])
{
printf("密码错误,转账失败!\n");
}
else
{
printf("输入转账金额:");
scanf("%lf",&money);
if(money>balance[whoout])
{
printf("余额不足,转账失败!\n");
}
else
{
printf("输入转入账号:");
scanf("%d",&id);
whoin = search(id);
if(whoin<0)
{
printf("转入账号不存在,转账失败!\n");
}
else
{
if(user[whoin][2]>0)
{
printf("转入账号异常,转账失败!\n");
}
else
{
printf("转入户主姓名:%s\n",name[whoin]);
balance[whoout] -= money;
balance[whoin] += money;
printf("转账后您账户余额为:%.2lf\n",balance[whoout]);
}
}
}
}
}
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",name[who]);
printf("请输入密码:");
iPass = inputPassword();
if(iPass != user[who][1])
{
printf("密码错误,不能继续操作!\n");
}
else
{
printf("新密码:");
newPass = inputPassword();
printf("确认密码:");
rePass = inputPassword();
if(newPass == rePass)
{
user[who][1] = newPass;
printf("修改成功!\n");
}
else
{
printf("两次输入不同,修改失败!\n");
}
}
}
return;
}
/*
功能:输入密码
返回值:整形密码值
说明: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))
{
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;
}
/*
功能:根据帐号查询用户,并返回用户下标
入口参数:需要查询用户的帐号
返回值:如果该用户存在,返回该用户在数组中的下标,否则返回负数(-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][0])
{
index = mid;
break;
}
else if(id<user[mid][0])
{
high = mid-1;
}
else
{
low = mid+1;
}
}
return index;
}
运行结果:
由于篇幅问题这里不做过多演示。
知识点总结:
1.函数 2.二维数组 3.以及提高篇和初步篇的许多知识点
学习心得:
这个代码虽然很复杂,但他让我明白模块话程序设计的重要性,模块化程序设计可以使你的程序结构更加清晰,我们将会在写代码的过程中不断提升,一定要坚持。由于提高篇所学的知识有限,在学完进阶篇后我们将再次对银行储蓄系统进行优化处理,提高篇也在此告一段落,让我们在进阶篇里不见不散!!