1、第十章 学生成绩管理综合实训,系统设计要求,系统设计及函数实现,参考程序,系统设计要求,本系统所要实现的功能 :,1新建学生信息(1)用来重新建立学生的信息记录;(2)若已经有记录存在,可以覆盖原记录或在原记录后面追加新记录,也可以将原有记录信息保存到另一个文件中,然后重新建立记录;(3)给出相关的提示信息;(4)及时更新存储标志。,2存储学生信息文件(1)可以将记录存储到指定文件名的文件中或存储到默认文件名的文件中;(2)将存储记录的文件进行存盘,并能根据文件保存是否成功而返回合适的值,以标识文件保存成功或失败。(3)如果写同名文件将替换原来文件的内容。,3读取学生信息文件(1)可以按默认文
2、件名或指定文件名将记录文件读入内存;(2)能根据读取情况返回合适的值,以标识文件读取成功或失败。(3)可以将指定或默认文件追加到现有记录之后,并能更新记录序号;(4)及时更新存储标志。,系统设计要求,4学生记录增加(1)可在已有记录后面追加新的记录;(2)可以随时增加新的记录,记录仅保存在结构数组中;(3)可以将一个文件读入,追加在已有记录之后;(4)如果已经采取文件追加的方式,在没有保存到文件之前,将继续保持文件追加状态,从而实现文件的连续追加;(5)如果没有记录存在,将给出相关的提示信息。5学生记录显示(1)若没有可以显示的记录,给出相关的提示信息;(2)可以随时显示内存中的记录;(3)能
3、够显示表头。,系统设计要求,6学生记录删除(1)可以按不同的方式将记录删除。比如:可以按”学号”、”姓名”等删除记录,但在彻底删除记录前应允许用户有后悔机会;(2)如果已经是空表,删除时应给出提示信息并返回主菜单;(3)如果没有要删除的信息,给出相关提示;(4)删除操作仅限于内存,只有执行存记录时,才能覆盖原有记录;(5)删除记录后应更新其它记录的序号;(6)更新存储标志。,系统设计要求,7学生记录修改(1)可以按”学号”、”姓名”等方式修改记录内容,在进行修改之前应先进行确认;(2)如果是空表,修改时应给出提示信息并返回主菜单;(3)如果没有找到要修改的信息,给出相关提示;(4)修改记录后应
4、更新记录的序号(如果对记录修改之后,影响记录的存储顺序的话);(5)更新存储标志。,系统设计要求,9学生记录排序(1)可以按”学号”进行升序或降序排序;(2)可以按”姓名”进行升序或降序排序;(3)可以按”名次”进行升序或降序排序;(4)如果属于选择错误,则立即退出排序;(5)更新存储标志。10头文件在头文件中应有函数原型的声明、数据结构及包含文件。,8学生记录查询(1)可以按”学号”、”姓名”等方式对学生记录进行查询;(2)能给出查询记录的信息;(3)如果查询的信息不存在,则给出相关提示信息。,系统设计要求,系统设计及函数实现,系统设计本系统的模块设计要求是: (1) 要求用多文件方式实现设
5、计,以避免因文件过大而带来的诸多不便。 (2) 要求在各文件内实现结构化设计。 (3) 每个模块作为一个单独的C文件。 (4) 宏和数据结构等均放在头文件中。,函数设计,1头文件student.h,在本系统中,每个学生的资料可以用一个STUDENT结构体变量保存,用STUDENT结构体数组全局变量record来保存一批学生的信息。用宏定义INITIAL_SIZE表示数组的初始大小。全局变量stunum表示数组中记录的学生数,arraysize是为数组分配的空间大小。全局变量savedtag是信息是否已被保存的标志,当数组内容被保存至文件后,设为”已保存”状态,当数组内容被修改后,设为”未保存”
6、状态。,头文件具体实现,#include #include #include #include #define INITIAL_SIZE 500#define SUBJECT_NUM 5struct student_info char xh20; char xm20; char xb2; float scoreSUBJECT_NUM; float sum; float average; int mc; ;,int saverecord(void);int loadrecord(void);void newrecord(void);void quit(void);int findrecord(c
7、har* target,int targettype,int from);int getmc(float sum);void copyrecord(STUDENT* str,STUDENT*dest);,typedef struct student_info STUDENT;extern int stunum;extern STUDENT * record;extern int savedtag;extern int arraysize;extern char *subject;void menu _ handle (void);int menu_select(void);void addre
8、cord(void);void modifyrecord(void);void displayrecord(viod);void queryrecord(void);void removerecord(void);void sortrecord(void);,2文件存储操作函数函数原型: int saverecord(void)功能:将记录存入默认文件stu_information中或者指定文件中。参数:void返回值:成功返回1,否则,返回-1。要求:能给出是否有记录可存、是否能正常建立或打开文件的提示信息,根据要求执行存入操作。,函数设计,分析:保存信息至文件的函数是通过fwrite函数一
9、次写入从record开始的stunum个STUDENT大小的字节。在进行记录的存储之前,应能先判断是否有可以存储的记录,该功能可以通过判断存储学生人数的变量stunum的值实现。若stunum为0,则没有可以存储的记录,否则,根据要求进行存储。可以将记录存入默认文件,也可以将记录存入指定文件,可以根据所输入的文件名的长短来决定将记录存入的文件若所输入的文件名的长度为0(表示直接按了回车),则表示将文件存入默认文件stu_information中,否则,表示存入指定的文件中。为了将记录正确写入默认或指定的文件中,用户应该对该文件有相应的权限,若不具备相应权限,则不能存入记录,否则,将记录存入到文
10、件中。,函数设计,int saverecord() FILE *fp; char fname30; if(stunum=0) printf(“没有记录可存!”); return -1; printf(“请输入要存入的文件名(直接回车将文件存入默认文件stu_information):”);gets(fname);,函数设计,if (strlen(fname)=0) strcpy(fname,”stu_information”);if (fp=fopen(fname,”wb”)=NULL) printf(“不能存入文件!n”); return -1; printf(“n存文件n”);fwrite
11、(record,sizeof(STUDENT)*stunum,1,fp);savedtag=0;return 1;,函数设计,3文件读取操作函数函数原型:int loadrecord(void)功能:将默认文件stu_information或者指定文件里的记录取入内存。参数:viod返回值:读取成功返回1,否则,返回-1。要求:给出是否有记录可读取、能否正常打开文件、是否覆盖已有记录及读取记录的条数的提示信息。,函数设计,文件读取操作函数具体实现,为了操作方便,在内存没有记录时,执行读取操作不询问是否覆盖信息。当内存又通过追加增加信息时,执行读取将是直接追加其后,不需要询问是否覆盖信息。当将多
12、个文件连续读取追加时,也不需要询问是否覆盖信息。,分析:文件读取信息函数需要根据情况进行相应的操作,情况比较复杂。将该函数设计为可以连续读入文件,后面的文件可以追加到前面的记录数组之后,从而形成一个更大的文件。,当对信息执行存储之后,如果再执行读取,则要询问是否覆盖信息。 在进行两个文件拼接时,需在内存无记录的情况下进行。此时需对读入的第二个文件进行询问。 如果要覆盖原来的记录,要先保存原记录,然后令stunum为0,否则,原来的stunum不变。在读取文件时,使用fread函数,每次读取sizeof(STUDENT)个字节,存放在数组recordstunum里面,并令stunum自加1,如此
13、下去,直到 文件读完为止。 取入文件涉及到重新排定名次的问题,需调用getmc函数对名次进行更新。,int loadrecord(void) FILE *fp; char fname30; char str5; if (stunum!=0 /*将读取的记录添加到现有记录之后*/else,printf(“请输入要读取的文件名,直接回车选择文件stu_information:”); gets(fname); if (strlen(fname)=0) strcpy(fname,”stu_information”);, if (savedtag=1) printf(“读取文件将更改原来的记录,是否保存
14、原来的记录?(Y/N)”); gets(str); if (str0)!=n ,printf(“n取文件n”); while (!feof(fp) if(fread(,if (fp=fopen(fname,”rb”)=NULL) printf(“打不开文件,请重新选择n”); return -1; ,4显示学生信息的函数,显示学生信息的函数具体实现,函数原型:void displayrecord(void)功能:显示内存里的记录信息。参数:void返回值:void 要求:显示内容及表头并给出是否有记录及记录条数的提示信息。分析:为了使所显示的内容清晰,应先将表头显示出来。该功能可通过输出与存放
15、学生信息的结构体变量的数据项相对应的内容实现。学生信息的显示可通过循环遍历数组中第0至第stunum-1个数组变量,逐条显示所有学生的信息。,void displayrecord(void) int i,j; if (stunum=0) printf(“没有可以显示的记录!”); return; printf(“学号t姓名t性别t”); for (j=0;j SUBJECT_NUM;j+) printf(“t%s”,subjectj); printf(“t总分t平均分t名次n”); for (i=0;istunum;i+) printf(“%st%st%s”,recordi.xh,record
16、i.xm,recordi.xb); for (j=0;j SUBJECT_NUM;j+) printf(“t%f”,recordi.scorej); printf(“t%ft%ft%dn”,recordi.sum,recordi.average,recordi.mc); ,5增加学生信息的函数,函数原型:void addrecord(void)功能:增加记录。参数:void返回值:void要求:将新记录追加在原记录之后,并对记录进行计数,而且能重新修改各记录中的名次。,函数设计,分析: 本函数用来在当前表的尾部增加新的信息,即在原有记录之后增加新信息。这只要将新的信息保存到recordstun
17、um中即可,同时完成stunum的自加1操作。若原来没有记录,则建立新表。 在增加了新的信息之后,所有记录当中的名次应根据新增加记录的总分情况进行重新评定。名次通过计算总分得来,该学生的名次是总分高于他的学生的总数加1,并且所有总分小于它的学生的名次也都加1,总分高于该生的学生名次保持不变。求每个学生的名次,我们用一个单独的函数getmc()实现。,增加学生信息函数具体实现,函数设计,void addrecord(void) char str10; int j; float mark,sum; if (stunum=0) printf(“原来没有记录,现在建立新表n”); else print
18、f(“下面在当前表的末尾增加新的记录n”); while (1) printf(“您要增加新的信息(Y/N)?”); gets(str); if (str0=n|str0=N) break; /*不再增加信息*/ printf(“请输入学号:”); gets(recordstunum.xh); printf(“请输入姓名:”);,gets(recordstunum.xm); printf(“请输入性别:”); gets(recordstunum.xb); sum=0; for (j=0;j SUBJECT_NUM;j+) printf(“请输入成绩:”); scanf(“%f”, ,print
19、f(“现在一共有%d条记录。”,stunum); savedtag=1; int getmc(float sum) int i; int count=0; /*总分大于sum的人数*/ for (i=0;isum) count+; return count+1;,6删除学生信息的函数函数原型:void removerecord(void)功能:删除指定的学生记录。参数:void返回值:void要求:根据给定的关键字,查找并删除记录,同时将后面的记录前移而且改变各记录的名次。,函数设计,分析: 若要删除指定的记录,首先要找到要删除的记录。在找到指定记录之后,为防止误操作,应先将相关的信息显示以便
20、让用户确认确实要删除。若确定删除,则令stunum-1,同时将排在该记录之后的所有记录前移,最后还应该修改其它记录的名次,排在删除记录前的记录的名次保持不变,而所有排在该记录之后的记录的名次值减1。查找指定记录的操作用一个独立的函数findrecord()实现。,函数设计,findrecord()函数: 函数原型: int findrecord(char *target,int targettype,int from)功能:查找指定的记录。参数: char *target:预查找记录的某一项与target相同 int targettype:指明通过那一项来查找,0为学号,1为姓名,2为名次 i
21、nt from:从第from个记录开始查找返回值:若找到指定记录则返回其记录号,否则,返回-1要求:根据给定的关键字,查找符合要求的记录的记录号,找不到返回-1。,函数设计,int findrecord(char *target,int targettype,int from) int i; for(i=from;istunum;i+) if (targettype=0,分析:该函数使用3个参数,第一个参数是字符target,表示预查找的记录的某一项与target相同;第二个参数是targettype,指明通过那一项来查找,0为学号,1为姓名,2为名次;第三个参数是from,表示从第from个
22、记录开始查找。当需要查找所有符合条件的记录时,只需先调用i=findrecord(targert,targettype,0),然后反复调用i= findrecord(targert,targettype,i+1)即可,这样每次找到的i就是符合条件的记录号。,删除学生信息函数具体实现,void removerecord(void) char str15; char target20; int type; int i,j; int tmpi; if (stunum=0) printf(“没有可删除的记录!”); return; while(1) printf(“请输入找到预删除记录的方式:n”);
23、 printf(“0按学号n”); printf(“1按姓名n”); printf(“2按名次n”); gets(str); if(str0=0) printf(“请输入该学生的学号:”); gets(target); type=0; ,else if(str0=1) printf(“请输入该学生的姓名:”); gets(target); type=1; else if(str0=2) printf(“请输入该学生的名次:”); gets(target); type=2; i=findrecord(target,type,0); if (i=-1) printf(“没有符合条件的记录!”);
24、while (i!=-1) printf(“学号t姓名t性别t”); for (j=0;j SUBJECT_NUM;j+) printf(“t%s”,subjectj); printf(“t总分t平均分t名次n”); printf(“%st%st%s”,recordi.xh, recordi.xm, recordi.xb);,for (j=0;jtmpi) recordj.mc-; i=findrecord(target,type,i+1); savedtag=1; ,在将指定记录删除后,应将该记录后面的记录依次前移,该前移操作实际上是用第i+1条记录覆盖第i条记录的内容,或者是说将第i+1条
25、记录的内容复制到第i条记录当中去。为了实现该项操作我们设计了一个独立函数copyrecord()。,copyrecord()函数 :,函数原型:void copyrecore(STUDENT *str2,STUDENT *str1)功能:将str2指向的一条记录复制到str1指向的记录。参数:STUDENT *str2:原记录 ,STUDENT *str1:目的记录 返回值:void要求:将原记录的内容正确复制到目的记录中去。,copyrecord()函数 具体实现,函数设计,void copyrecord(STUDENT *str2,STUDENT *str1) int j; strcpy(
26、str1.xh, str2.xh); strcpy(str1.xm, str2.xm); strcpy(str1.xb, str2.xb); for (j=0;j SUBJECT_NUM;j+) str1.scorej=str2.scorej; str1.sum=str2.sum; str1.average=str2.average; str1.mc=str2.mc;,函数设计,7查询指定学生信息的函数函数原型:void queryrecord(void)功能:查找满足条件的记录。参数:void要求:可以按学号、姓名或名次来查询学生信息,并可以给出相关提示。分析: 该函数是利用findreco
27、rd()来查询所有符合条件的记录。基本思想在findrecord()中已给出,此不赘述。,查询指定学生信息函数具体实现,函数设计,void queryrecord(void) char str5; char target20; int type; int count; int i,j; if (stunum=0) printf(“没有可供查询的记录!”); return; while (1) printf(“请输入查询的方式:(直接回车则结束查询)n”); printf(“0按学号n”); printf(“1按姓名n”); printf(“2按名次n”); gets(str); if (str
28、len(str)=0) break;,函数设计,if(str0=0) printf(“请输入该学生的学号:”); gets(target); type=0; else if(str0=1) printf(“请输入该学生的姓名:”); gets(target); type=1; else if(str0=2) printf(“请输入该学生的名次:”); gets(target); type=2; i=findrecord(target,type,0); count=0; printf(“学号t姓名t性别t”);,printf(“t%ft%ft%dn”,recordi.sum,recordi.av
29、erage,recordi.mc); i=findrecord(target,type,i+1); if (count=0) printf(“没有符合条件的记录!n”); else printf(“共找到了%d符合条件的记录!n”,count); ,for (j=0;j SUBJECT_NUM;j+) printf(“t%s”,subjectj); printf(“t总分t平均分t名次n”); while(i!=-1) count+; printf(“%st%st%s”,recordi.xh, recordi.xm, recordi.xb); for (j=0;j SUBJECT_NUM;j+
30、) printf(“t%f”,recordi.scorej);,8修改指定学生信息的函数函数原型:void modifyrecord(void)功能:找到并修改指定学生的信息参数:void返回值:void要求:要求可以按学号、姓名或名次找到要修改的记录,而且能给予用户后悔的机会,在确定修改后,如果修改的信息影响学生的名次,则应能更改所有学生的名次信息。,函数设计,修改指定学生信息函数具体实现,分析:在本函数中同样要用到findrecord()函数来找到要修改的记录。对于找到的每一条记录都要显示出来让用户确认是否要修改。若确定要修改,则直接输入学生的信息,然后根据新记录中的总分来重新计算平均分和
31、计算名次,并修改其它记录的相应名次。即:该学生的名次是所有总分高于该生的学生人数的总数加1,并且将原来名次排在被修改记录之后,而其总分大于等于修改后记录总分的记录的名次减1,将原来名次排在被修改记录之前,而其总分小于修改后记录总分的记录名次增1。,函数设计,void modifyrecord(void) char str5; char target20; int type; int i,j; int tmpi; float sum,mark; int count=0; /*总分大于sum的总人数*/ if (stunum=0) printf(“没有可供修改的记录!”); return; whi
32、le (1) printf(“请输入找到要修改记录的方式:(直接回车则结束修改)n”); printf(“0按学号n”); printf(“1按姓名n”); printf(“2按名次n”); gets(str); if (strlen(str)=0) break;,if(str0=0) printf(“请输入该学生的学号:”); gets(target); type=0; else if(str0=1) printf(“请输入该学生的姓名:”); gets(target); type=1; else if(str0=2) printf(“请输入该学生的名次:”); gets(target);
33、type=2; i=findrecord(target,type,0); if (i=-1) printf(“没有符合条件的学生记录!”);printf(“学号t姓名t性别t”); for (j=0;j SUBJECT_NUM;j+) printf(“t%s”,subjectj);,printf(“t总分t平均分t名次n”); /*显示表头*/ printf(“%st%st%s”,recordi.xh, recordi.xm, recordi.xb); for (j=0;j SUBJECT_NUM;j+) printf(“t%f”,recordi.scorej); printf(“t%ft%f
34、t%dn”,recordi.sum,recordi.average,recordi.mc); printf(“确实要修改该学生的信息吗?(Y/N)”); gets(str); if (str0=Y|str0=y) tmpi=recordi.mc; printf(“请重新输入该学生的信息:n”); printf(“请输入学号:n”); gets(recordi.xh); printf(“请输入姓名:n”); gets(recordi.xm); printf(“请输入性别:n”); scanf(“%s”,for (j=0;jtmpi ,9排序学生记录函数函数原型:void sortrecord(v
35、oid)功能:对记录进行排序。参数:void返回值:void要求:可以按学号、姓名或名次对学生记录进行升序或降序排序。分析:该功能的实现,实际上是在该函数中针对不同的排序方式反复运用前面课本中所讲的排序功能。,排序学生记录函数具体实现,函数设计,void sortrecord(void) char str5; int i,j; STUDENT tmps; if (stunum=0) printf(“没有可供排序的记录!n”); return; printf(“请输入排序的方式:n”); printf(“1 按学号升序进行排序。n”); printf(“2 按学号降序进行排序。n”); prin
36、tf(“3 按姓名升序进行排序。n”); printf(“4 按姓名降序进行排序。n”); printf(“5 按名次升序进行排序。n”); printf(“6 按名次将序进行排序。n”); gets(str);,函数设计,if (str06) return; for(i=0;i0)|(str0=2 ,函数设计,10新建学生记录函数函数原型:void newrecord(void)功能:新建立学生的信息记录。参数:void返回值:void要求:根据需要调用saverecord()函数,若原来的信息没有保存,则保存原来信息,然后重新输入新信息。分析:在实现了saverecord()函数和addr
37、ecord()函数后,本函数的实现思想及算法就比较简单了。,新建学生记录函数具体实现,函数设计,void newrecord(void) char str5; if (stunum!=0) if (saved=1) printf(“现在已有记录,是否保存原有记录?(Y/N)n”); gets(str); if(str0!=N|str0!=n) saverecord(); stunum=0; addrecord(); ,函数设计,11结束程序运行函数函数原型:void quit(void)功能:结束程序的运行。参数:void返回值:void要求:在结束程序运行之前,要决定是否对修改的记录进行存储
38、。分析:该函数功能比较简单。根据需要确定是否要调用saverecord()函数将记录进行存储。,结束程序运行函数,函数设计,void quit(void) char str5; if (savedtag=1) printf(“是否要保存现有记录?(Y/N)”); gets(str); if(str0!=N|str0!=n) saverecord(); exit(0); ,函数设计,12菜单选择函数函数原型:int menu_select(void)功能:接受用户选择的命令代码,返回处理不同的菜单函数的整数值代码。参数:void返回值:int要求:只允许选择规定键,若选择不符合规定,则要求重新输
39、入,并返回命令代码的整数值。分析:本函数根据用户选择的代码不同,而选择不同的菜单处理函数。而且要求用户的输入应在正确的范围之内。,菜单选择函数具体实现,函数设计,int menu_select(void) int s; printf(“n”); printf(“t0:增加学生信息。n”); printf(“t1:修改学生信息。n”); printf(“t2:显示学生信息。n”); printf(“t3:查询学生信息。n”); printf(“t4:删除学生信息。n”); printf(“t5:排序学生信息。n”); printf(“t6:保存学生信息。n”); printf(“t7:读取学生信息。n”); printf(“t8:新建学生信息。n”); printf(“t9:结束程序运行。n”); printf(“t请选择对应功能的数字:09。n”); scanf(“%d”,s); if (s9) printf(“n选择错误,请重新选择!”); return s; ,