C语言课程设计报告个人消费管理系统.doc
目 录 1 需求分析1 1.1问题描述1 1.2 输入数据要求1 1.3 输出数据要求1 1.4 开发环境和工具1 1.5 成员分工1 2总体设计2 2.1 总体设计思路2 2.2 模块结构图2 2.3 模块说明3 3 详细设计4 3.1 数据类型定义5 3.2 打开模块实现6 3.3 更新模块实现6 3.4 统计模块实现74测试结果及分析125 总结17参考文献18附录191 需求分析1.1问题描述随着社会经济的发展,大学生已经成为这个社会上一个很重要的团体,而他们的消费情况也越来越多的被更多的人关注,大学生自己也应当管理好自己的消费内容,让自己的消费更加合理化,提高自己生活的质量。在这种情况下,就需要一个软消费表信息:编号,时间,类别,品名,单价,数量,金额对学生每天的消费情况作一个较为详细的统计,该系统可以实现由计算机代替人工执行一些复杂而且较为繁琐的操作,从而使大学生们能够更为便捷地管理自己的消费,也为其他的一些调查研究提供了方便。1.2 输入数据要求可录入消费者当天的消费情况,消费表信息包括:编号,时间,类别,品名,单价,数量,金额消费信息存放在文件中,以编号的先后顺序存放1.3 输出数据要求打开:显示文件中的所有消费记录,提供分页显示保存:将消费表的更新结果存入文件更新:可插入、删除、修改各消费记录查找:按时间,类别、品名查找消费记录排序:按时间,类别,金额统计:求各类别消费品的总金额,求各月的消费总金额,求各年消费总金额1.4 开发环境和工具开发环境:Windows 2000开发工具:Visual C+ 6.01.5 成员分工刘倩钰:主函数模块,保存模块,排序模块刘冬梅:打开模块,更新模块,数据统计模块2总体设计2.1 总体设计思路1. 设计思路个人消费系统管理包含的操作有:打开、保存、更新、查找、排序和统计。在程序开始运行的时候从文件加载已有数据,并输出可以进行的操作提供给用户,1、打开:显示文件中的所有消费记录,通过取模运算提供分页显示功能2、保存:以wb模式打开文件,将修改的内容写入文件3、更新:可插入、删除、修改各消费记录4、查找:按时间,类别、品名查找消费记录5、排序:按时间,类别,金额6、统计:分别按类别、月、年统计消费品的总金额2. 数据存储为了操作的方便,用typedef自定义了一些数据类型,其中包含一个长度为MAX的数组,用来存放从文件中读取的数据。2.2 模块结构图根据需求将系统划分为六个功能模块,函数之间的调用关系如图2.1所示。 Main主函数 SaveRec保存 ShowRec打开ShowTotal统计 CalleditUpdateRec更新 SortRec排序 FindRec查找图2.1 个人消费系统的模块结构图Calledit:在主函数的控制下,调用各个模块1)ShowRec: 输出已存在的记录。2)SaveRec:保存更新的内容。3)UpdateRec: 按一定的方式更新记录。这个模块下包含三个子模块 插入:Insert 删除: DeleteByNum 修改:Modify 4)FindRec: 按一定的方式查找记录。 这个模块下也包含三个子模块 按日期查找:FindBytime 按类型查找:FindByType 按名称查找: FindByName 5)SortRec: 按一定的方式查找记录。 这个模块下也包含三个子模块 按日期查找:SortBytime 按类型查找:SortByType 按名称查找: SortBySum6)ShowTotal: 显示各种统计的结果2.3 模块说明(以从上至下,从左至右的顺序说明)2.3.1:打开模块:自定义函数:ShowRec( )函数功能:输出已存在的历史记录 无参函数:无参数2.3.2:更新模块:自定义函数:UpdateRec( ) 函数功能: 可对输入数据实现插入,删除,修改操作。无参函数:void类型,无返回值。调用Insert()函数,Delete()函数和Modify()2.3.2.1插入模块:自定义函数: Insert() 函数功能:实现插入新的信息并保存到记录中,其中插入的数据最后金额可以自己运算出来,插入的记录保存在尾部。 无参函数:返回值为int 类型2.3.2.2删除模块:自定义函数; RemoveByNum(int num) 函数功能:对所选信息按编号删除,删除过后,后面记录到编号将全部减1,记录的编号即下标。 输入参数:int类型,返回值是int类型。num-int类型,表示define.h文件中定义的类型Rec_Con中数组record的下标,删除过后,后面记录到编号将全部减1,记录的编号即下标。后面的记录全部向前移动一个距离。2.3.2.3修改模块:自定义修改函数:Modify() 函数功能:按名称修改记录,先判断要查找的名称所对应的记录是否存在,不存在则直接返回。 无参函数:返回值为int型2.3.3:统计模块:函数原型:ShowTotal( ) 函数功能:显示统计结果无参函数:void 类型,无返回值。2.3.3.1 按类型统计:ShowType( )函数功能:按类型显示统计结果无参函数; void 类型,无返回值。2.3.3.2按月显示统计结果:函数原型:ShowMonth() 函数功能:忽略了年,按月显示统计结果无参函数:void 类型,无返回值。2.3.3.3按年显示统计结果:函数原型:ShowYear() 函数功能:按年显示统计结果无参函数:void 类型,无返回值。3 详细设计3.1 数据类型定义关键数据都定义在define.h里#define MAX 100 /人为定义的一个上限typedef struct / 定义记录结构体,并重新命名类型名int num; /编号char times11; /时间char types20; /类别char name20; /品名double price; /单价int quantity; /数量double sum; /金额Rec;typedef struct / 定义操作结构体,并重新命名类型名Rec recordMAX+1; /定义结构体变量,可操作的记录总数,下标为0和MAX不存放记录int total_count; /整型变量,记录总数Rec_Con,*PRec_Con;union /定义一个查找时使用的共用体char times11; /存放按时间查找时输入的时间char types20; /存放按类型查找时输入的类型char name20; /存放按名称查找时输入的名称found;/下面三个变量都是全局变量FILE *fp; /定义文件指针Rec_Con rs; /用来定义变量,避免了动态分配中的安全问题PRec_Con records=&rs; /定义操作指针变量int type_count=0; /记录里相关的类型数目int year_count=0; /记录里相关的年数目char *ptypeMAX+1; /字符型变量,记录里相关的类型char pyearMAX+15; /字符型变量,记录里相关的年char tmp5; /字符型变量,取年份所用char tmp015,tmp025; /存放Get4的返回值,在比较时使用char c; /字符型变量,临时使用变量,没有固定的限制3.2 ShowRec模块实现1.算法思想:按编号, 时间,类别,品名,单价,数量,金额的顺序打开文件,并显示结果。其中提供分页显示功能。2.具体实现:void OpenFile() /打开文件函数,以二进制append形式操作if(fp=fopen("consum","ab+")=NULL)printf("File open fail!n");return ;void ShowI(int i) /根据i值输出记录printf("记录:nn"); printf("n编号 时间 类别 品名 单价 数量 金额 n");printf("%-5d% -10s%15s%15s %8.2f%5d%8.2fn",records->recordi.num,records->recordi.times,records->recordi.types,records->recordi.name,records->recordi.price,records->recordi.quantity,records->recordi.sum);int ShowRec() /显示所有记录int i;for(i=1;i<=records->total_count;i+)printf("%-5d%-10s%15s%15s%8.2f%5d%8.2fn",records->recordi.num,records->recordi.times,records->recordi.types,records->recordi.name,records->recordi.price,records->recordi.quantity,records->recordi.sum);if(i%10)=0) /实现分页功能printf("Press any key show next pagen");getchar();/cls();return 1;3.3更新模三个块:更新包括内容;插入,删除,查找1.算法思想:利用三个自定义函数:int Insert() /插入函数,int DeleteByNum() /按编号删除,int Modify() /按名称修改记录。插入过程用MAX控制插入信息的范围;删除时按编号删除,删除过后,后面记录到编号将全部减1,记录的编号即下标。修改时先利用int FindByName(char *name);按名称查找记录函数,找到相关信息,若无此信息直接跳转,否则修改。2.具体实现:int Insert() /插入函数if(records->total_count=(MAX-1)printf("数据已达可控范围,不能再插入!n"); return 0;int i=(+records->total_count);records->recordi.num=i; /编号是连续的, printf("n 时间 类别 品名 单价 数量 金额 n");scanf("%s%s%s%lf%d",records->recordi.times,records->recordi.types,records->recordi.name,&records->recordi.price,&records->recordi.quantity);if(strlen(records->recordi.times)!=10|records->recordi.times4!='-'|records->recordi.times7!='-') /简单的判断一下日期是否符合格式要求printf("时间格式错误,请重试!n");records->total_count-;return 0;records->recordi.sum = records->recordi.price * records->recordi.quantity; /金额是计算所得#ifdef DEBUGShowI(i);#endifreturn 1;int RemoveByNum(int num) /删除过后,后面记录到编号将全部减1,记录的编号即下标int i;if(num<1|num>records->total_count)printf("位置错误!n");return 0;for(i=num;i<records->total_count;i+) /i后面的记录全部向前移动一个距离records->recordi.num=records->recordi+1.num-1;strcpy(records->recordi.times,records->recordi+1.times);strcpy(records->recordi.types,records->recordi+1.types);strcpy(records->recordi.name,records->recordi+1.name);records->recordi.price=records->recordi+1.price;records->recordi.quantity=records->recordi+1.quantity;records->recordi.sum=records->recordi+1.sum;return 1;int DeleteByNum() /按编号删除int num;printf("请输入要删除记录的编号:");scanf("%d",&num);if(!(RemoveByNum(num)printf("程序执行错误,删除失败!n");return 0;elsereturn 1;int FindByName(char *name); /声明按名称查找记录函数,下面就要使用到int Modify() /按名称修改记录 int i;char names20;printf("请输入要修改的记录名称:");scanf("%s",names);i=FindByName(names);if(!i) /判断要查找的名称所对应的记录是否存在,不存在则直接跳转return 0;printf("请输入所需信息:n");printf("n 时间 类别 品名 单价 数量 金额 n");scanf("%s%s%s%lf%d",records->recordi.times,records->recordi.types,records->recordi.name,&records->recordi.price,&records->recordi.quantity);records->recordi.sum = records->recordi.price * records->recordi.quantity;return 1;3.4 ShowTotal模块实现ShowTotal包括三种方式:类型、月、年,以类型为例说明1、算法思想为确保不会溢出,保存统计结果的total数组定义长度为MAX,接下来对记录集进行遍历,并与ptype数组中统计的类型进行比对,按照对应的下标将统计结果加到total数组中,这样做便于输出时一一对应。(复杂算法用流程图辅助说明)2、具体实现(给出实现算法的关键代码)按类型显示统计结果void ShowType() int i=1,j=0;double totalMAX=0.0; /定义保存统计结果的数组while(i<=records->total_count&&j<type_count) /遍历并记录统计结果if(strcmp(ptypej,records->recordi.types)=0)totalj += records->recordi.sum;i+;elsej+;/输出统计结果printf("按类型统计结果如下:n"); for(i=0;i<type_count;i+)printf("%s:t%8.2fn",ptypei,totali);printf("n");4测试结果及分析1. 打开程序,进入主界面2. 输入“1”,打开历史消费记录:3. 输入“1”,测试更新模块:4. 输入“1”,插入信息数据:5. 输入“2”,按编号删除数据:6. 输入“3”,修改历史信息;7. 输入“0”,返回主菜单:8. 输入“6”,测试统计结果;9. 输入“1”,按类别统计结果:10,输入“2”,按月显示统计结果:10. 输入“3”,按年显示统计结果:5.总结为期四天的课程设计结束了,其中的酸甜苦辣恐怕只有经历过的人才会懂得。其实课程设计的题目老师很早就发下来了,但由于自己时间观念不强,没有提前准备,所以搞得开始做的时候发现很多东西都不会,又临时学占用了很多时间。下次一定要吸取教训,不能临时抱佛脚啊!此次课程设计虽然很难,但也让我学到了很多东西。它让我学会的不仅仅是怎么去编一个大的程序,更让我学会了怎样去开始做一件事,怎样规划自己的时间做好一件事。刚开始看到提示真的是一片茫然,不知如何下手,然后经过自己上网查阅和看了一些课程设计的书之后渐渐有了一点眉目。这些对我的自学能力锻炼很大。另外,在编程过程中也遇到了很多很多的问题,比如统计模块的数组元素定义错误导致结果不能运行,等等、我知道自己在C这条路上还有很长的路要走,但这次得课程设计无疑给了我更加坚定走下去的信心,他让我体会了迷茫,烦躁,痛苦、但它也让我学会了淡定,从容,坚持、它带给我四个晚上熬夜到两点半的记录,它也带给了我看到程序运行出结果的喜悦、程序中的各种各样的错误曾经让我失望甚至绝望,想放弃,但同组的我们相互鼓励视乎又看到了希望、在这次设计中还请教了几个学计算机的高中同学,这让我学会了要善于向他人求助,同时好久没联系的同学通过这次设计也加强了我们的联系。我认为这次课程设计对我们的锻炼很大,它处在离考试只有三四天的时候,这么紧迫的时刻有这么短的时间,这大大的锻炼了我们的时间安排能力和不急不躁心态。另外程序中还有很多不完善的地方,希望以后加强学习能够将其改正至臻完善吧。非常感谢这次设计中给我们帮助的同学,学长和老师!6.参考文献:参考文献:1谭浩强等.C语言程序设计教程.北京:高等教育出版社,1997.2谭浩强.C程序设计.第2版.北京:清华大学出版社,1999.3徐士良.C程序设计教程附录主函数源程序代码:#include<stdio.h>#include<stdlib.h>#include<string.h>#include<windows.h>#include"defined.h"#include"arc.h"int main(void)int chioce; /开始的时候使用char试了一下,有些功能不能实现records->total_count=0;#ifdef DEBUGprintf("jskdljf:%dn",records->total_count);#endifvoid calledit(int chioce); /声明中间过程函数OpenFile("ab+"); /打开文件并初始化数组,即加载数据while(!feof(fp)fread(&records->record+rs.total_count,sizeof(Rec),1,fp); /record0中并不存放记录数据,以后作为查找到前哨站fclose(fp);#ifdef DEBUGprintf("jskdljf:%dn",records->total_count);ShowRec();#endifwhile(1) /用一个死循环实现循环操作,只有选择退出的时候才退出system("cls");printf("-消费记录系统-n");printf("1-打开n");printf("2-保存n");printf("3-更新n");printf("4-查找n");printf("5-排序n");printf("6-统计n");printf("Other-退出n"); printf("请输入操作序号:");scanf("%d",&chioce);system("cls");if(chioce>6|chioce<1)break;elsecalledit(chioce); /调用中间过程函数return 0;void calledit(int chioce)void UpdateRec();void FindRec();void SortRec();switch(chioce)case 1:ShowRec();break;case 2:SaveRec();break;case 3:UpdateRec();break;case 4:FindRec();break;case 5:SortRec();break;case 6:ShowTotal();break;default:break;return ;void UpdateRec()int mc;while(1)printf("1-插入n");printf("2-删除n");printf("3-修改n");printf("0-离开n");printf("请选择:");scanf("%d",&mc);if(mc=1)Insert();else if(mc=2)DeleteByNum();else if(mc=3)Modify();else if(mc=0)break;elseprintf("输入有误,请重试!n");continue;scanf("%c",&c);void FindRec()int mc;int i;while(1)printf("1-按时间查找n");printf("2-按类型查找n");printf("3-按名称查找n");printf("0-离开n");printf("请选择:");scanf("%d",&mc);if(mc=1)printf("请输入时间(format:2011-12-31):");scanf("%s",found.times);if(strlen(found.times)!=10|found.times4!='-'|found.times7!='-') /简单的判断一下日期是否符合格式要求printf("时间格式错误,请重试!n");continue;i=FindByTime(found.times);if(i)ShowI(i);else if(mc=2)printf("请输入类型:");scanf("%s",found.types);i=FindByType(found.types);if(i)ShowI(i);else if(mc=3)printf("请输入名称:");scanf("%s",found.name);i=FindByName(found.name);if(i)ShowI(i);else if(mc=0)break;elseprintf("输入有误,请重试!n");continue;scanf("%c",&c);system("cls");void SortRec()int mc;while(1)printf("1-按时间排序n");printf("2-按类型排序n");printf("3-按金额排序n");printf("0-离开n");printf("请选择:");scanf("%c",&mc);if(mc=1)SortByTime();else if(mc=2)SortByType();else if(mc=3)SortBySum();else if(mc=0)break;elseprintf("输入有误,请重试!n");continue;scanf("%c",&c);system("cls");Define.h文件代码:#define MAX 100 /人为定义的一个上限/#define DEBUG /控制调试信息的输出typedef structint num; /编号char times11; /时间char types20; /类别char name20; /品名double price; /单价int quantity; /数量double sum; /金额Rec;typedef structRec recordMAX+1; /可操作的记录总数int total_count; /记录总数Rec_Con,*PRec_Con;union char times11; char types20; char name20; found;/下面三个变量都是全局变量FILE *fp; /定义文件指针Rec_Con rs; PRec_Con records=&rs; /定义指针变量int type_count=0; /记录里相关的类型数目int year_count=0; /记录里相关的年数目char *ptypeMAX+1; /记录里相关的类型char pyearMAX+15; /记录里相关的年char tmp5; /取年份所用char c; /使用频率很高的一个变量,后来好像又没声明用了,先放着吧arc.h文件代码:#include<string.h>void OpenFile(char * mode) /打开文件函数,以二进制append形式操作if(fp=fopen("consum",mode)=NULL)printf("File open fail!n");return ;void ShowI(int i) /根据i值输出记录printf("记录:%-5d%-10s%15s%15s%8.2f%5d%8.2fn",records->recordi.num,records->recordi.times,records->recordi.types,records->recordi.name,records->recordi.price,records->recordi.quantity,records->recordi.sum);int ShowRec() /显示所有记录int i;printf("tt-打印记录-nn");printf("编号t 日期tt 类型t 名称t 单价 数量t 金额n");for(i=1;i<=records->total_count;i+)printf("%-5d %-10s%15s%15s%8.2f%5d %8.2fn",records->recordi.num,records->recordi.times,records->recordi.types,records->recordi.name,records->recordi.price,records->recordi.quantity,records->recordi.sum);if(i%9)=0) /实现分页功能printf("Press any key show next page");getchar();getchar(); /不明白这里为什么非要两次getchar()?system("cls"); printf("tt-打印记录-nn");printf("编号t 日期tt 类型t 名称t 单价 数量t 金额n");printf("Press any key into main menu");return 1;int SaveRec() /保存更新int i;OpenFile("wb");for(i=1;i<=records->total_count;i+)fwrite(&records->recordi,sizeof(Rec),1,fp);fclose(fp);return 1;int Insert() /插入函数if(records->total_count=(MAX-1)printf("数据已达可控范围,不能再插入!n"); return 0;int i=(+records->total_count);records->recordi.num=i; /编号是连续的,1nprintf("请输入所需信息(顺序为:时间,类别,品名,单价,数量):");scanf("%s%s%s%lf%d",records->recordi.times,records->recordi.types,records->recordi.name,&records->recordi.price,&records->recordi.quantity);i