程序设计报告( 2012 / 2013 学年第一学期)题目:电信计费系统客户帐单管理专业计算机科学与技术学生姓名班级学号指导教师指导单位计算机软件教学中心日期 2012年9月28日电信计费系统客户帐单管理课题内容和要求客户帐单管理是电信计费系统必备的重要功能模块,主要负责对电信各类客户每月帐单的增加、修改、删除、查询、备份等管理工作。
本课题以中国电信企业客户帐单管理模块原型参照,要求基于单链表结构对文件存储的客户帐单数据进行排序、查找、计算、显示等造作。
通过此可以,熟练掌握单链表结构、文件读写、函数调用等知识,以及查找、排序典型算法的设计与应用。
需求分析要能提供以下几个基本功能:(1)用户资料文件与话单文件由程序设计人员预先从键盘上录入,用户资料文件中的数据记录不得少于30条,话单文件中的数据记录不得少于200条,且必须有跨月份、跨年份的通话记录话单。
(2)首先从硬盘读入用户资料和话单记录(3)对话单进行计费处理,输出费用详单到指定的文件。
格式如下:电话号码用户姓名月租费功能费市内电话费用长途电话费用合计84370000 ChenChao 25 6 34.2 19.6 84.8 84370000 ChenChao 25 6 ………………(4)异常话单提示。
有的话单可能是错误的,找出这些话单,不参加计费,另存为一个文件。
可能的出错话单有:一个号码的通话时长大于三天。
图1:功能图(1)提供可操作的主菜单:输出个菜单,用于显示以从文件中加载的总客户信息和若干个可选的功能选项。
根据客户输入的选项来运行不同的功能,运行不同的函数。
(2)显示所有用户功能:根据选项可将文本里的用户信息在屏幕上显示出来。
(3)显示所有帐单功能:可将文本里的通话帐单在屏幕上显示出来。
(4)添加用户功能:可在屏幕上进行直接操作,将新的用户信息添加到记录里。
(5)添加帐单功能:操作同上,可将新的通话记录添加到记录里。
(6)按用户名查找功能:输入记录里的用户名,可以查找与之相关的通话帐单信息。
(7)按电话号码查找功能:输入记录里已有的电话号码,可以查找相应的通话帐单记录。
(8)输出话费功能:在操控台和文本中,直接输出所有的话费详情。
概要设计函数结构图图2:主程序图图3:操作菜单系统图4:显示所有信息菜单图5:插入数据菜单图6:计算话费函数1.顾客类(Customer.h)#include<iostream.h>#include<iomanip.h>#include<string.h>class Customer //顾客类{protected:char name[20]; //顾客姓名char phonenum[14]; //顾客电话号码char laidian; //顾客是否订来电显示public:Customer(char *n="",char *ph="",char l=0); //构造函数Customer(const Customer &cu); //拷贝构造函数~Customer(); //析构函数char* getname(); //提取姓名char* getphone(); //提取电话号码char getlaidian(); //提起是否订来电显示friend ostream & operator << (ostream &,const Customer &); //友元函数重载输出运算符"<<"friend istream & operator >> (istream &,Customer &); //友元函数重载输入运算符">>"};//Customer类的函数的实现Customer::Customer(char *n,char *ph,char l){strcpy(name,n);strcpy(phonenum,ph);laidian=l;}Customer::Customer(const Customer &cu){strcpy(name,);strcpy(phonenum,cu.phonenum );laidian=idian;}Customer::~Customer(){}char* Customer::getname(){return name;}char* Customer::getphone(){return phonenum;}char Customer::getlaidian(){return laidian;}ostream & operator<<(ostream & out ,const Customer & cust) {cout.setf(ios::left);out<<setw(20)<<;out<<setw(15)<<cust.phonenum;out<<setw(2)<<idian<<'\n';return out;}istream & operator >> (istream & in ,Customer & cust){in>>;in>>cust.phonenum;in>>idian;return in;}2.账单类(Bill.h)#include<iostream.h>#include<string.h>int mon_day(int y,int m); //判断一个月有几天class Bill //账单类{protected:char zhujiao[14]; //主叫号码char beijiao[14]; //被叫号码char starttime[15]; //开始时间char endtime[15]; //结束时间public:Bill(char *zh="",char *be="",char *st="",char *en=""); //构造函数Bill(const Bill &bi); //拷贝构造函数~Bill(); //析构函数char* getzhujiao(); //提取主叫号码char* getbeijiao(); //提取被叫号码int gettime(); //提取通话时间int getdate(); //提取通话日期char* getstart(); //提取开始时间char* getend(); //提取结束时间friend int panduan(char *m,char *n); //友元函数判断账单记录是否错误,若错误,返回-1;若正确,返回通话时间friend ostream & operator << (ostream &,const Bill &); //友元函数重载输出运算符"<<"friend istream & operator >> (istream &,Bill &); //友元函数重载输入运算符">>"};//Bill类的函数的实现Bill::Bill(char *zh,char *be,char *st,char *en) {strcpy(zhujiao,zh);strcpy(beijiao,be);strcpy(starttime,st);strcpy(endtime,en);}Bill::Bill(const Bill &bi){strcpy(zhujiao,bi.zhujiao);strcpy(beijiao,bi.beijiao);strcpy(starttime,bi.starttime);strcpy(endtime,bi.endtime);}Bill::~Bill(){}char* Bill:: getzhujiao(){return zhujiao;}char* Bill:: getbeijiao(){return beijiao;}int Bill::gettime(){return panduan(starttime,endtime);}int Bill::getdate(){int date[6];for(int i=0;i<6;i++){date[i]=starttime[i]-48;}int dat=date[0]*100000+date[1]*10000+date[2]*1000+date[3]*100+date[4]*10+date[5];return dat;}char* Bill::getstart(){return starttime;}char* Bill::getend(){return endtime;}ostream & operator << (ostream &out,const Bill &bi){out<<bi.zhujiao<<'\t';out<<bi.beijiao<<'\t';out<<bi.starttime<<'\t';out<<bi.endtime<<'\n';return out;}istream & operator >> (istream &in,Bill &bi){in>>bi.zhujiao;in>>bi.beijiao;in>>bi.starttime;in>>bi.endtime;return in;}int panduan(char *m,char *n){int a[14],b[14]; //建立两个int数组接收开始时间和终止时间for(int i=0;i<14;i++) //把char数组转换为int数组{a[i]=m[i]-48;b[i]=n[i]-48;}int y1,mon1,d1,h1,min1,s1; //开始时间年,月,日,时,分,秒int y2,mon2,d2,h2,min2,s2; //终止时间年,月,日,时,分,秒int x,y; //x把开始时间d1,h1,min1统一转换为分钟形式,便于计算;y则为终止时间y1=a[0]*1000+a[1]*100+a[2]*10+a[3]; //把a[n]转换为6个整数:年,月,日,时,分,秒mon1=a[4]*10+a[5];d1=a[6]*10+a[7];h1=a[8]*10+a[9];min1=a[10]*10+a[11];s1=a[12]*10+a[13];x=d1*24*60+h1*60+min1;y2=b[0]*1000+b[1]*100+b[2]*10+b[3]; //把b[n]转换为6个整数:年,月,日,时,分,秒mon2=b[4]*10+b[5];d2=b[6]*10+b[7];h2=b[8]*10+b[9];min2=b[10]*10+b[11];s2=b[12]*10+b[13];y=d2*24*60+h2*60+min2;if( mon1>12 ||mon1<1 ||mon2>12 ||mon2<1 ||h1>23 ||h1<0 ||h2>23 ||h1<0 ||min1>59 ||min1<0 ||min2>59 ||min2<0 ||s1>59 ||s1<0 ||s2>59 ||s2<0 ||d1<1 ||d2<1 ||d1>mon_day(y1,mon1) ||d2>mon_day(y2,mon2)){return -1; //若“月,日,时,分,秒”超出范围,则返回-1 }else{if(y1==y2){if(mon1==mon2) //同一个月内的情况{if( (y-x<24*60*3) && (y-x>0) ) //通话时间在3天内{if(s1<s2) //通话时间不足一分钟,当一分钟计算return y-x+1;elsereturn y-x;}else if(y==x) //开始时间的分钟等于终止时间的分钟{if(s2>s1) //开始时间小于终止时间return 1;else //开始时间等于或大于终止时间算错误,不计入话费,返回-1{//cout<<"账单通话时间记录错误!"<<endl;return -1;}}else //开始时间大于终止时间或通话时间大于3天,算错误,返回-1 {//cout<<"账单通话时间记录错误!"<<endl;return -1;}}else //跨月的情况{if(mon2-mon1!=1) //终止时间月份只可能比开始时间月份大1{//cout<<"账单通话时间记录错误!"<<endl;return -1;}else{if(s1<s2){return y-x+1+mon_day(y1,mon1)*24*60;}else{return y-x+mon_day(y1,mon1)*24*60;}}}}else //跨年的情况{if(y2-y1==1&&mon1==12&&mon2==1) //年和月只有这一种情况是对的{if(s1<s2){return y-x+1+31*24*60;}else{return y-x+31*24*60;}}else{//cout<<"账单通话时间记录错误!"<<endl;return -1;}}}}int mon_day(int y,int m){int list[12];list[0]=31;if(y%4==0&&y%100!=0||y%400==0) //判断是否是闰年list[1]=29;elselist[1]=28;list[2]=31;list[3]=30;list[4]=31;list[5]=30;list[6]=31;list[7]=31;list[8]=30;list[9]=31;list[10]=30;list[11]=31;return list[m-1]; //返回y年m月有多少天}源程序代码#include"Customer.h"#include"Bill.h"#include<fstream.h>char Printmenu(); //显示菜单选项char Printzimenu(); //显示子菜单选项,查询话费和输出错误话单void PrintCustomer(); //显示所有用户信息void PrintBill(); //显示所有帐单信息void EnterCustomer(); //添加用户信息void EnterBill(); //添加账单信息void SearchBIll1(); //按用户名查找帐单,并显示该账户所有帐单void SearchBIll2(); //按电话号码查找账单,并显示该账户所有帐单void PrintAll(); //不操作,输出所有花费信息void jifei(Customer *cust,Bill *bill,int m,int n); //计算话费,并输出到文本void PrintWrongBill(Bill *bill,int n); //输出错误账单信息bool compare(int m,int *p,int n); //如果m与数组p[n]的任何一个数都不相等,返回falseint chongtu(Bill *bill,int n,Bill bi,int i); //判断bi账单是否与其他账单冲突,若冲突,则返回-1double yuezhu=25.0 , laixian=6.0 , local=0.1 , idfee=0.7 ; //月租,来电显示费用及市内电话和长途电话单价const int Max=1000; //最大数组长度int main(){char n;while((n=Printmenu())!='0'){switch(n){case '1': PrintCustomer(); break;case '2': PrintBill(); break;case '3': EnterCustomer(); break;case '4': EnterBill(); break;case '5': SearchBIll1(); break;case '6': SearchBIll2(); break;case '7': PrintAll(); return 0;default: cout<<"输入有误:请重新输入!\n";}}return 0;}char Printmenu(){cout<<"\t 模拟电信计费系统"<<endl;cout<<"\t************************************************************"<<endl;cout<<"\t *********** 输入1:显示所有用户信息**********"<<endl;cout<<"\t *********** 输入2:显示所有帐单信息**********"<<endl;cout<<"\t *********** 输入3:添加用户信息**********"<<endl;cout<<"\t *********** 输入4:添加帐单信息**********"<<endl;cout<<"\t *********** 输入5:按用户名查找帐单**********"<<endl;cout<<"\t *********** 输入6:按电话号码查找帐单**********"<<endl;cout<<"\t *********** 输入7:不操作,输出所有话费信息**********"<<endl;cout<<"\t *********** 输入0:结束操作**********"<<endl;cout<<"\t************************************************************"<<endl;cout<<"\n\n使用说明:\n 错误话单存在此程序目录下WrongBill.txt中\n 费用详单存在此程序目录下allmessage.txt中\n";cout<<" 费用详单目录为:电话号码—用户姓名—月份—月租费—功能费—市话—长途—合计\n";cout<<"注:时间24:00算错误;计费月份按拨打电话时间算\n\n";char n;cout<<"请输入操作选项:\n";cin>>n;return n; //返回操作选项}char Printzimenu(){cout<<"\t************************************************************"<<endl;cout<<"\t *********** 输入0:返回上一级菜单**********"<<endl;cout<<"\t *********** 输入1:显示查询用户话费信息**********"<<endl;cout<<"\t *********** 输入2:显示查询用户错误账单信息**********"<<endl;cout<<"\t************************************************************"<<endl;char n;cout<<"请输入操作选项:\n";cin>>n;return n; //返回操作选项}void PrintCustomer(){Customer cust[Max];ifstream inf("Customer.txt");int m=0;while(!inf.eof()) //录入顾客信息到Customer类的数组cust中{inf>>cust[m];cout<<cust[m]; // 输出顾客信息m++; //有m-1个顾客}inf.close();Bill bill[Max];ifstream infs("Bill.txt");int n=0;while(!infs.eof()) //录入账单记录到Bill类的数组bill中{infs>>bill[n];//cout<<bill[n]; //输出账单信息n++; //有n个账单记录}infs.close();char z;while((z=Printzimenu())!='0'){if(z=='1')jifei(cust,bill,m,n);else if(z=='2')PrintWrongBill(bill,n);elsecout<<"输入错误,请重新输入!"<<endl;}}void PrintBill(){Customer cust[Max];ifstream inf("Customer.txt");int m=0;while(!inf.eof()) //录入顾客信息到Customer类的数组cust中{inf>>cust[m];//cout<<cust[m]; // 输出顾客信息m++; //有m-1个顾客}inf.close();Bill bill[Max];ifstream infs("Bill.txt");int n=0;while(!infs.eof()) //录入账单记录到Bill类的数组bill中{infs>>bill[n];cout<<bill[n]; //输出账单信息n++; //有n个账单记录}infs.close();char z;while((z=Printzimenu())!='0'){if(z=='1')jifei(cust,bill,m,n);else if(z=='2')PrintWrongBill(bill,n);elsecout<<"输入错误,请重新输入!"<<endl;}}void EnterCustomer(){Customer cust[Max];int n;cout<<"请输入用户数量:\n";cin>>n;cout<<"请输入客户信息:\n客户姓名—电话号码—是否有来电显示(Y/N)\n";for(int i=0;i<n;i++)cin>>cust[i];ofstream outf;outf.open("Customer.txt",ios::app); //用app方式打开文本,能直接在文本尾加入信息for(i=0;i<n;i++)outf<<cust[i];cout<<"输入成功!"<<endl;outf.close();}void EnterBill(){Bill bill[Max];int n;cout<<"请输入帐单数量:\n";cin>>n;cout<<"请输入帐单信息:\n主叫号码—被叫号码—开始时间—结束时间\n时间格式为连续14个数:年月日时分秒(20080201134421)\n";for(int i=0;i<n;i++)cin>>bill[i];ofstream outf;outf.open("Bill.txt",ios::app);for(i=0;i<n;i++)outf<<bill[i];cout<<"输入成功!"<<endl;outf.close();}void SearchBIll1(){char name[20];cout<<"请输入用户姓名:\n";cin>>name;Customer cust[Max];Customer cu[1]; //保存输入的用户名的类Bill bill[Max];ifstream inf("Customer.txt");ifstream infs("Bill.txt");int m=0;while(!inf.eof()) //录入顾客信息到Customer类的数组cust中{inf>>cust[m];//cout<<cust[m]; // 输出顾客信息m++; //有m-1个顾客}inf.close();int n=0;while(!infs.eof()) //录入账单记录到Bill类的数组bill中{infs>>bill[n];//cout<<bill[n]; //输出账单信息n++; //有n个账单记录}infs.close();Bill bi[Max]; //保存输入用户名的账单类信息int k=0;cout<<"该用户的账单信息为:"<<endl;bool p=false,t=false,u=false;for(int i=0;i<m-1;i++){if(strcmp(cust[i].getname(),name)==0){cu[0]=cust[i];for(int j=0;j<n;j++){if(strcmp(cust[i].getphone(),bill[j].getzhujiao())==0){cout<<bill[j];t=true;bi[k]=bill[j];k++;}elsecontinue;}u=true;}elsep=true;}if(!t&&u)cout<<"\t该用户没有信息!"<<endl;if(p&&!u)cout<<"输入错误:没有该用户信息!"<<endl;char z;while((z=Printzimenu())!='0'){if(z=='1')jifei(cu,bi,2,k);else if(z=='2')PrintWrongBill(bi,k);elsecout<<"输入错误,请重新输入!"<<endl;}}void SearchBIll2(){Customer cust[Max];ifstream inf("Customer.txt");int m=0;while(!inf.eof()) //录入顾客信息到Customer类的数组cust中{inf>>cust[m];//cout<<cust[m]; // 输出顾客信息m++; //有m-1个顾客}inf.close();char phone[20];cout<<"请输入用户电话号码:\n";cin>>phone;Bill bill[Max];ifstream infs("Bill.txt");int n=0;while(!infs.eof()) //录入账单记录到Bill类的数组bill中{infs>>bill[n];//cout<<bill[n]; //输出账单信息n++; //有n个账单记录}infs.close();int b=0;cout<<"该用户的账单信息为:"<<endl;bool r=true;Bill bi[Max];int k=0;for(int j=0;j<n;j++)if(strcmp(bill[j].getzhujiao(),phone)==0){cout<<bill[j];b=false;bi[k]=bill[j];k++;}if(b){cout<<"输入错误:没有该号码的信息"<<endl;}Customer cu[1];for(int i=0;i<m-1;i++){if(strcmp(cust[i].getphone(),phone)==0){cu[0]=cust[i];}}char z;while((z=Printzimenu())!='0'){if(z=='1')jifei(cu,bi,2,k);else if(z=='2')PrintWrongBill(bi,k);cout<<"输入错误,请重新输入!"<<endl;}}void PrintAll(){Customer cust[Max];Bill bill[Max];ifstream inf("Customer.txt");ifstream infs("Bill.txt");int m=0;while(!inf.eof()) //录入顾客信息到Customer类的数组cust中{inf>>cust[m];//cout<<cust[m]; // 输出顾客信息m++; //有m-1个顾客}inf.close();int n=0;while(!infs.eof()) //录入账单记录到Bill类的数组bill中{infs>>bill[n];//cout<<bill[n]; //输出账单信息n++; //有n个账单记录}infs.close();jifei(cust,bill,m,n);}void jifei(Customer *cust,Bill *bill,int m,int n){cout<<"姓名\t 电话号码日期月租来电市话长途总计"<<endl;ofstream ou("allmessage.txt");ofstream outw("WrongBill.txt");for(int i=0;i<m-1;i++) //计算话费{int a[Max]={0}; //int数组,用于记录已计算过的月份if(cust[i].getlaidian()=='Y')laixian=6;elselaixian=0;for(int j=0;j<n;j++)if( strcmp( cust[i].getphone() , bill[j].getzhujiao() ) == 0 ) //找出cust[i]客户的所有帐单{double shihua=0,changtu=0,zong; //一个月的市话费、长途费及总电话费if(compare(bill[j].getdate(),a,n )) //判断在记录a[n]中是否存在bill[j].getdate() 记录,若存在,则跳过这条账单信息continue;else{for(int k=j;k<n;k++){if(( bill[j].getdate() == bill[k].getdate() )&&( strcmp( cust[i].getphone() , bill[k].getzhujiao() ) == 0 ) ) //若bill[k]的月份与主叫电话号码都相同,则计算这个月的电话费{if(bill[k].gettime()>=0&&chongtu(bill,n,bill[k],k)==1){if( strlen( bill[k].getbeijiao() )>8 )changtu += bill[k].gettime()*idfee;elseshihua += bill[k].gettime()*local;a[j]=bill[j].getdate(); //把bill[j]记录到a[n]中}else{outw<<bill[k];}}}zong=yuezhu+laixian+shihua+changtu;//操控台输出cout.setf(ios::left);cout<<setw(15)<<cust[i].getname();cout<<setw(12)<<cust[i].getphone();cout<<setw(8)<<bill[j].getdate();cout<<setw(5)<<yuezhu<<setw(5)<<laixian;cout<<setw(8)<<shihua<<setw(8)<<changtu;cout<<setw(8)<<zong<<'\n';//文本输出ou<<cust[i].getphone();ou<<setw(15)<<cust[i].getname();ou<<setw(10)<<bill[j].getdate();ou<<setw(5)<<yuezhu<<setw(5)<<laixian;ou<<setw(8)<<shihua<<setw(8)<<changtu;ou<<setw(8)<<zong<<'\n';}}}}outw.close();ou.close();}void PrintWrongBill(Bill *bill,int n){bool a=true;for(int i=0;i<n;i++){if(bill[i].gettime()==-1||chongtu(bill,n,bill[i],i)==-1){cout<<bill[i];a=false;}}if(a)cout<<"该用户没有错误信息!"<<endl;}bool compare(int m,int *p,int n){for(int i=0;i<n;i++){if(m==p[i])return true;elsecontinue;}return false;}int chongtu(Bill *bill,int n,Bill bi,int i) //判断bi的时间是否和其他账单冲突,若冲突,则返回-1{for(int j=0;j<n;j++){if(j!=i){if(strcmp( bi.getzhujiao(),bill[j].getzhujiao() )==0){if(strcmp(bi.getstart(),bill[j].getstart())==1){if(strcmp(bi.getstart(),bill[j].getend())==1)return 1;elsereturn -1;}else if(strcmp(bi.getstart(),bill[j].getstart())==-1){if(strcmp(bill[j].getstart(),bi.getend())==1)return 1;elsereturn -1;}elsereturn -1;}}else continue;}return 1;}测试数据及其结果分析显示的主菜单界面:(1)显示用户信息测试:(2)显示账单信息测试:(3)添加用户信息测试:(4)添加账单信息测试:(5)按用户名查找账单测试:(6)按电话号码查找账单测试:(7)输出话费信息测试:调试过程中的问题(1)输出格式不对,不整齐:用了setw()函数和ios::left解决。