1、C语言模拟ATM机一、 实验目的通过设计一个ATM机模拟操作的程序,全面运用课程的主要知识点,巩固对模块化程序设计、文件操作的理解,提高软件编程能力。二、 实验要求要求每人独立完成设计,编写一个ATM机模拟操作程序。提交实验报告、源程序、程序运行截图。1. 程序具有以下功能:1) 登录系统:输入卡号(16位)、密码(6位),只有卡号、密码同时正确(信息存放在文件中)才能进入系统;2) 操作功能:查询:查询的存款余额。按任意键返回主菜单;修改密码:连续输入两次新密码(两次输入结果完全相同)后,新密码取代旧密码,并存放到信息文件中。之后可返回主菜单;取款:输入50或100的倍数模拟为取款金额,请注
2、意:取款金额不得超过余额,操作后余额应相应减少。之后可返回主菜单;存款:输入50或100的倍数模拟为存款金额,现假设存款金额不得超过20000元,操作后余额应相应增加。之后可返回主菜单;转帐:先输入一个号(已保存在信息文件中),然后显示一部分用户名(如:*山),储户确认无误后,再输入50或100的倍数模拟为转帐金额,请注意:转帐金额不得超过余额,操作后余额应相应减少。之后可返回主菜单;退出:退出系统。2. 定义如下结构体类型:“日期”结构体Date:成员有:year(年)、month(月)、day(日);“”结构体Account:成员有:no(号)、name()、balance(余额)“存取款
3、记录”结构体Record,:成员有:no(号)、date(日期)、number(数量,可正可负,正表示存款,负表示取款)3. 信息存放在accounts.txt文件,储户存取款记录保存在records.txt中;4. 采用模块化程序设计方法来实现,即main()显示主菜单、调用相关函数实现相应功能,起主控作用。5. 附加功能:储户一天输错密码的次数不能超过3次,否则无法进入系统;应避免输入存款金额、取款金额为负数等异常情况的出现。三、 程序已经实现的功能点实验要求均已实现。此外还有:1. 卡号、密码位数输错时,提示并返回重新输入,2. 简化取款时的输入3. 转账时,转账账户为原账户时,提示并返
4、回重新输入4. 在多个模块中添加了退出功能,无需回到主菜单即可退出系统5. 在查询模块中加入了存取记录明细查询四、 程序运行截图及说明1. 登陆模块1) 卡号不为16位时,输出提示并返回重新输入2) 卡号不存在时,输出提示并返回重新输入3) 密码位数输错时,输出提示并返回重新输入密码,不作为密码错误处理,也无需重新输入卡号4) 密码连续输错三次时,锁定卡号,今日无法登录2. 登录后的系统主菜单3. 查询模块1) 查询模块主菜单2) 查询明细4. 取款模块1) 当余额不足50时,无法进入取款模块2) 取款模块主菜单3) 便捷取款4) 手动输入取款金额输错时,返回重新输入5) 取款后的菜单6) 取
5、款完成后选择显示余额5. 存款模块1) 解决输入错误问题2) 存款成功6. 密码修改模块1) 原密码输错时,返回重新输入,不提供退出功能2) 密码位数输错、两次输入新密码不一致时,返回重新输入3) 输入无误时7. 转账模块1) 转入卡号位数输错时,提示并返回重新输入2) 未查找到该卡号时,输出提示,提供退出功能,键入除*外任意键重新输入3) 转入卡号与当前卡号相同时,输出提示4) 输出转入卡账户的部分用户名5) 解决转账金额输错的问题6) 转账成功五、 采用的主要技术1. 宏定义:由于本程序的printf语句较多,为了便于编写,故用PR代替2. 链表:账户信息采用链表结构,存动态分配,使用存无
6、限制,可以扩展到所有可以使用的存3. switch多分支选择结构4. 文本文件的读写5. 利用time.h头文件,简便地获取系统日期6. system()函数和fflush()函数的简单使用六、 设计心得1. 各函数的功能要相对独立,逐步求精。2. 如果函数的参数为指针,且仅作为输入参数,则应在类型前加const,以防止该指针在函数体被意外修改附:程序代码/* 运行本程序前,请在本程序根目录下建立文件“accounts.txt”,格式如下:* 卡号1(16位) 1 账户余额1 密码1* 卡号2(16位) 2 账户余额2 密码2* */#include #include #include #in
7、clude #define PR printftypedef struct short int year; short int month; short int day;Date;/日期typedef struct Account char no17; char name20; double balance; char password7; struct Account* next;Account;typedef struct char no17; Date date; double number;Record;/记录Account *head = NULL;/账户信息链表表头Account
8、*now_acc = NULL;/当前账户 /*函数申明*/void InitializeAccount();/导入账户信息/-char LockAccountTopic(const char *no);/检测账户是否被锁定void LockAccount(const char *no);/锁定账户/-void Login();/登录Account* FindbyNo(const char *no);/查找卡号对应账户信息在链表中的地址/-void Demand();/查询主菜单void PrintBalance();/输出余额void Printetail();/输出明细/-void Mod
9、ifiy();/修改密码/-void Withdrawal();/取款主界面void WithdrawalOt(long money);/取款/-void Depositing();/存款/-void Transfer();/转账char *CoverUpName(const char *name);/掩盖部分用户名/-void Log(double money, const char *no);/将存款记录写入文件中void Save();/保存账号信息void FreeData();/释放链表空间/-int main() char choose; InitializeAccount();/
10、导入账户信息 Login();/登录 do system(cls); PR(tt n); PR(tt 请选择服务 n); PR(tt+-+-+ +-+-+n); PR(tt|5|33转 账| |查 询32|1|n); PR(tt+-+-+ +-+-+nn); PR(tt +-+-+n); PR(tt |取 款32|2|n); PR(tt +-+-+nn); PR(tt +-+-+n); PR(tt |存 款32|3|n); PR(tt +-+-+nn); PR(tt+-+-+ +-+-+n); PR(tt|0|33退出系统| |修改密码32|4|n); PR(tt+-+-+ +-+-+nn);
11、 PR(请选择服务:); fflush(stdin); choose = getchar(); switch (choose) case 1: Demand(); break;/查询 case 2: Withdrawal(); break;/取款 case 3: Depositing(); break;/存款 case 4: Modifiy(); break;/修改密码 case 5: Transfer(); break;/转账 case 0: FreeData(); return 0;/退出 default: printf(输入错误,请重新选择n); /解决用户可能的输入错误 system(
12、pause); while (1); return 0;/*导入账户信息*/void InitializeAccount() FILE *fp; Account temp; PR(系统初始化中,请稍侯.n); if (!(fp = fopen(accounts.txt, r) PR(07);/响铃 PR(读取信息失败,请在本程序根目录建立信息文件(accounts.txt)。格式如下:n); PR(卡号1(16位) 1 账户余额1 密码1n); PR(卡号2(16位) 2 账户余额2 密码2n); PR(n); exit(0); while (fscanf(fp, %s %s %lf %s,
13、temp.no, temp.name, &temp.balance, temp.password) = 4) /读入账户信息 Account *t = head; head = (Account*)malloc(sizeof(Account); *head = temp; head-next = t; fclose(fp); system(cls);/清除“系统初始化中,请稍侯.”提示/*登录*/void Login() do char no17; system(cls); PR(nnnnttt请输入16位卡号:); fflush(stdin); gets(no); if (strlen(no
14、) != 16) PR(卡号应为16位n); system(pause); system(cls); continue; if (LockAccountTopic(no) /当该卡号被锁定时 PR(07);/响铃 PR(nn您的登陆密码今日连续输错三次,已被临时锁定,次日将自动解锁,或当日持本人有效证件到柜台办理密码重置。nn); system(pause); continue; if (!(now_acc = FindbyNo(no)/将卡号对应信息在链表的地址赋给now_acc /当卡号不存在时 PR(nn此卡号不存在,请重新输入!nn); system(pause); continue;
15、 char a = 2;/计算剩余可输错密码次数 do char password7; system(cls); PR(nnnnttt请输入16位卡号:%sn, now_acc-no); PR(nnttt请输入6位密码:); fflush(stdin); scanf(%s, password); if (strlen(password) != 6) PR(密码应为6位n); system(pause); system(cls); continue; if (!strcmp(now_acc-password, password) return;/密码正确,回到主函数 if (a = 0) /输错
16、密码三次,锁定账户 PR(nn您的登陆密码今日连续输错三次,已被临时锁定,次日将自动解锁,或当日持本人有效证件到柜台办理密码重置。nn); LockAccount(no); break; PR(07);/响铃 PR(密码输入错误,您还可以尝试%c次nn, a-); system(pause); while (1); while (1);/*检测账户是否被锁定*/char LockAccountTopic(const char *no)/被锁定则返回0 FILE *fp; char CruuentTime11;/当前日期 char ArchiveTime11;/锁卡档案日期 char t17;
17、if (!(fp = fopen(LockAccount.txt, a+)/课本上讲以“a”方式打开文件,若文件不存在,会出错。VS2013实测,并不会出错,而是新建。为免第一次使用本程序时,根目录下没有LockAccount.txt而导致出错,故此处不采用“r”方式。 PR(07);/响铃 PR(LockAccount.txt无法打开nn); exit(0); rewind(fp); /获取当前日期 time_t rawtime; struct tm * timeinfo; time(&rawtime);/获取时间,以秒计,从1970年1月一日起算,存于rawtime timeinfo =
18、localtime(&rawtime);/转为当地时间,tm 时间结构 strftime(CruuentTime, 11, %Y-%m-%d, timeinfo);/格式化时间 fgets(ArchiveTime, 11, fp);/获取锁卡档案时间 if (strcmp(ArchiveTime, CruuentTime) /若无锁卡档案,或锁卡档案日期与当前日期不同,则无账户被锁定 fclose(fp); return 0; while (fscanf(fp, %s, t) = 1) if (!strcmp(t, no) /锁卡档案中,包含此卡号,返回1 fclose(fp); return
19、 1; fclose(fp); return 0;/锁卡档案中,不含此卡号,返回0/*查找卡号对应账户信息在链表中的地址*/Account* FindbyNo(const char *no) for (Account *i = head; i; i = i-next) if (!strcmp(i-no, no) return i; return NULL;/*锁定账户*/void LockAccount(const char *no) FILE *fp; char CruuentTime11;/当前日期 char ArchiveTime11;/锁卡档案日期 if (!(fp = fopen(L
20、ockAccount.txt, r) PR(07);/响铃 PR(error on open LockAccount.txt file!nn); exit(0); /获取当前日期 time_t rawtime; struct tm * timeinfo; time(&rawtime);/获取时间,以秒计,从1970年1月1日起算,存于rawtime timeinfo = localtime(&rawtime);/转为当地时间,tm 时间结构 strftime(CruuentTime, 11, %Y-%m-%d, timeinfo);/格式化时间 fgets(ArchiveTime, 11, f
21、p); fclose(fp); if (strcmp(ArchiveTime, CruuentTime) /若档案日期与当前日期不同,则说明档案已过期或无档案,新建记录。 if (!(fp = fopen(LockAccount.txt, w) PR(07);/响铃 PR(error on open LockAccount.txt file!nn); exit(0); fprintf(fp, %sn, CruuentTime); else/若记录日期与当前日期相同,则追加记录 if (!(fp = fopen(LockAccount.txt, a) PR(07);/响铃 PR(error on
22、 open LockAccount.txt file!nn); exit(0); /添加记录 fprintf(fp, %sn, no); fclose(fp);/*查询函数主菜单*/void Demand() char choose; do system(cls); PrintBalance(); PR(ttttt +-+-+n); PR(ttttt |查询明细32|1|n); PR(ttttt +-+-+nn); PR(tt+-+-+ +-+-+n); PR(tt|0|33退出系统| | 返 回 32|2|n); PR(tt+-+-+ +-+-+nn); PR(请选择服务:); fflush
23、(stdin); choose = getchar(); switch (choose) case 1: Printetail(); break;/查询明细 case 2: return;/返回 case 0: FreeData(); exit(0);/退出系统 default: printf(输入错误,请重新选择n); /解决用户可能的输入错误 system(pause); while (1);/*输出余额*/void PrintBalance() system(cls); PR(nnnnnnnnntt+-+-+n); PR(tt| 您的账户余额 32|%-15.2f|n, now_acc-
24、balance); PR(tt+-+-+nnnnnnn);/*输出明细*/void Printetail() FILE *fp; Record temp; int flag = 1;/当有记录时,置0 if (!(fp = fopen(records.txt, r) PR(无存取记录,请进行存取后再使用本功能nn); system(pause); return; system(cls); PR(+-+-+n); PR(| 日期 | 存取金额 |n); PR(+-+-+n); while (fscanf(fp, %s%d-%d-%d%lf, temp.no, &temp.date.year, &temp.date.month, &temp.date.day, &temp.number) = 5) if (!(strcmp(temp.no, now_acc-no) flag = 0; PR(|%d-%d-%d|%-14.2f|n, temp.date.year, temp.date.month, temp.date.day, temp.number); PR(+-+-+n); PR(| 日期
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1