学生成绩管理程序
一. 功能设计要求
设计要求实现的功能较多,所以将他们分为几个部分叙述。
1.建立文件
(1)可以使用默认文件名或指定文件名将记录存储到文件;
(2)文件保存成功返回0,失败返回-1;
(3)设置保存标志savedtag作为是否已对记录进行存储操作的信息;
(4)写同名文件将覆盖原来文件的内容。
2.增加学生纪录
(1)可在已有记录后面追加新的记录;
(2)可以随时用它增加新的记录,它们仅保存在结构数组中;
(3)可以将一个文件读入,追加在已有记录之后;
(4)如果已经采取用文件追加的方式,在没有保存到文件之前,将继续保持文件追加状态,以便实现连续文件追加操作方式;
(5)如果没有记录存在,给出提示信息。
3.新建学生信息文件
(1)用来重新建立学生信息记录;
(2)如果已经有记录存在,可以覆盖原记录或者在原记录后面追加,也可以将原有记录信息保存到一个指定文件,然后重新建立记录;
(3)给出相应的提示信息。
4.显示记录
(1)如果没有记录可供显示,给出提示信息;
(2)可以随时显示内存中的记录;
(3)显示表头。
5.文件存储
(1)可以按默认名字或指定名字存储记录文件;
(2)存储成功返回0,否则返回-1;
(3)更新存储标志。
6.读取文件
(1)可以按默认名字或指定名字将记录文件读入内存;
(2)读取成功返回0,否则返回-1;
(3)可以将指定或默认文件追加到现有记录的尾部;
(4)可以将文件连续追加到现有记录并更新记录的名次;
(5)更新存储标志。
7.删除记录
(1)可以按“学号”、“姓名”或“名次”方式删除记录;
(2)给出将被删除记录的信息,经确认后再删除;
(3)如果已经是空表,删除时应给出提示信息并返回住菜单;
(4)如果没有要删除的信息,输出没有找到的信息;
(5)应该更新其他记录的名次;
(6)删除操作仅限于内存,只有执行存记录时,才能覆盖原记录;
(7)更新存储标志。
8.修改记录
(1)可以按“学号”、“姓名”或“名次”方式修改记录;
(2)给出将被修改记录的信息,经确认后进行修改;
(3)如果已经是空表,应给出提示信息并返回住菜单;
(4)如果没有找到需要修改的信息,输出提示信息;
(5)应该同时更新其他记录的名次;
(6)修改操作仅限于内存,只有进行存储操作时,才能覆盖原记录;
(7)更新存储标志。
9.查询记录
(1)可以按“学号”、“姓名”或“名次”方式查询记录;
(2)能给出查询记录的信息;
(3)如果查询的信息不存在,输出提示信息。
10.对记录进行排序
(1)可以按学号进行升序或降序排序;
(2)可以按名称进行升序或降序排序;
(3)可以按名次进行升序或降序排序;
(4)如果属于选择错误,可以立即退出排序;
(5)更新存储标志。
11.头文件
(1)使用条件编译定义头文件;
(2)函数原型声明;
(3)数据结构及包含文件。
12.测试程序
(1)应列出测试大纲对程序进行测试;
(2)应保证测试用例测试到程序的各种边缘情况。
以上是基本要求,希望通过对本题设计的理解,重新考虑如何改进设计。
二.总体设计
最初的整体规划只是说明它们的可行性,不要求制定函数的具体实现,甚至不需要考虑函数原型。完成整体规划后,可以按照流程描述各个模块之间的接口功能。
本课程设计对模块设计的要求如下:
(1)要求使用多文件方式实现设计;
(2)要求在各个文件内实现结构化设计
(3)每个模块作为一个单独的C文件,每个文件内的函数如表12-1所示,表中给出了各个函数的功能说明;
(4)宏和数据结构等放在头文件中,并使用条件编译。
本设计有5个C 文件(17个函数)和一个头文件组成,每个C文件都代表着某特定的功能,它们的关系如表12-1所示。本章的设计将更加注意模块化,以便展示C 语言的编程风格。
程序包含文件的存、取过程。它的功能就是按输入顺序建立记录。如果原来没有记录文件,可以重新建立一个文件;如果已经有记录,可以先把文件内容输入,然后把新记录追加到原来记录的尾部;也可以单独建立新文件,以后再使用读取文件的方法拼接。
由上述功能分析可以看到它的全貌。因为它有并列选择,所以可以用选择菜单方便地实现。这个菜单具有10个选择项,用switch语句可以实现这些选择。可以用简洁的伪码对它们进行描述,因为并不复杂,所以不再赘述。
表12-1 文件及函数组成
三.函数设计
为一些函数设计两个返回值一曲分歧执行结果是否正确。每个学生信息资料用一个stuinfo结构来保存,用stuinfo数组全局变量records来保存一批学生的信息资料。
其中的宏定义INITIIAL-SIZE表示数组初始大小,当已经分配的数组大小不够用时,将真假数组的大小,INCR-SIZE为当每次增加的大小。全局变量numstus表示数组中记录的学生数,arraysize视为数组分配的空间大小。全局变量savedtag是信息是否已保存的标志,当属组内容被文件保存值文件后,设为“已保存”状态,当数组内容被修改之后,设为“未保存”状态。
下面分别描述这些函数,建立它们的函数原型。
1.文件存储操作函数
函数原型:int saverecords(void)
功能:将记录存入默认文件stu_info或者指定文件。
参数:void
返回值:成功0,失败-1
工作方式:数组records被保存至指定文件。
要求:报告是否有记录可存、是否能正常建立或打开文件、根据要求执行存
入操作并报告存入记录的条数。
2.文件读取操作函数
函数原型:int loadrecords(void)
功能:将默认文件stu_info或者指定文件里的记录取入内存。
参数:void
返回值:成功0,失败-1
工作方式:records将为从指定文件中读取出的记录。
要求:报告是否有记录可存、能否能正常打开文件、是否覆盖已有记录以及读取记录的条数。
3.显示所有学生信息的函数
函数原型:int display(void)
功能:显示内存里的记录信息
参数:void
返回值:void
工作方式:从头部开始逐个显示记录内容
要求:报告是否有记录及记录条数和内容。
4.增加信息函数
函数原型:void addrecord(void)
功能:增加记录
参数:void
返回值:void
工作方式:从尾部开始逐个追加记录。
要求:将新记录追加在记录尾部,并对记录进行计数。
5.打印表头函数
函数原型:void showtable(void)
功能: 打印表头
参数:void
返回值:void
工作方式:输出一行表头信息
要求:将表头按制表符打印要求。
6.输出在记录中按升序排序的位置
函数原型:void getindex(float sum)
功能: 找出总分为sum在第0至numstus-1个记录中按升序排序的
参数:float,预找出其位置的总分
返回值:int,sum在第0至numstus-1个记录中按升序排序的
工作方式:查找并计数
要求:输出位置整数
7.删除记录函数
函数原型:void removerecord(void)
功能: 删除内存数组中的制定记录
参数:void
返回值:void
工作方式:根据给定的关键字,查找符合的记录并删除。
要求:将后面的记录前移,同时改变名次并给出相关信息。
8.查找指定记录函数
函数原型:void findrecord(char*,int,int)
功能:查找指定的记录。
参数:char*target:预查找记录的某一项与target相同
intb targettype:表明通过哪一项来查找,0为学号,1为姓名,2为名
次
int form:从第from个记录开始找
返回值:int找到的记录的序号,若找不到则返回-1。
工作方式: 根据给定的关键字,查找符合记录的序号。
要求:找不到则返回-1。
9.查询指定学生信息的函数
函数原型:void queryinfo(void)
功能: 将一个文件的内容追加到另一个文件的尾部。
参数:void
返回值:void
工作方式:可以按照学号、姓名或名次来查询。
要求:打印查询到的学生的信息或给出相关信息。
10.记录复制函数
函数原型:void copyrecord(stuinfo*,stuinfo*)
功能: 将stc指向的一条记录复制给dest指向的记录。
参数:stuinfo*src:源记录stuinfo*dest:目的记录。
返回值:void
工作方式:将源记录逐条复制到目的记录
要求:正确复制字符串。
11.修改指定学生信息函数
函数原型:void modifyrecord(void)
功能: 找到指定记录并修改
参数:void
返回值:void
工作方式: 可以按照学号、姓名或名次找到要修改的记录,确认后方可修改。要求:同时需调整名次
12.学生信息排序函数
函数原型:void sortinfo(void)
功能:对记录进行排序
参数:void
返回值:void
工作方式: 可以按照学号、姓名或名次排序。
要求:升序或降序排序。
13.菜单处理函数
函数原型:void handle_menu(void)
功能:处理选择的菜单命令。
参数:void
返回值:void
工作方式:根据命令,调用相应函数。
要求:给出结束信息。
14.菜单选择函数
函数原型:int menu_select(void)
功能:接受用户选择的命令代码。
参数:void
返回值:int
工作方式:返回命令代码的整数值。
要求:只允许选择规定键,如果输入不合要求,则提醒用户重新输入。
15.新建学生信息记录的函数
函数原型:void newrecords(void)
功能:重新建立输入信息记录。
参数:void
返回值:void
工作方式:根据修要调用saverecords函数。
要求:若原来信息没有保存,则保存原来的信息,然后重新输入信息记录。
16.结束程序运行函数
函数原型:void quit(addr*)
功能: 结束程序运行.
参数:void
返回值:void
工作方式:根据要求决定在推出前是否将修改的记录存入文件。
要求:结束运行之前,询问是否对修改的记录进行存储。
17.主函数
函数原型:void main(void)
功能: 控制程序。
参数:void
返回值:void
要求:管理菜单命令并完成初始化。
18.头部文件
文件名称:student.h
功能:声明函数原型,包含文件及自定义宏和数据结构。
要求:报告是否能正常打开文件执行存入操作及记录的条数。
三.参考程序
1.student.h文件
#ifndef H_STUDENT_HH
#define H_ STUDENT_HH
#include
#include
#include
#include
#define INITIAL_SIZE 100 //树组初始大小
#define INCR_SIZE 50 //数组每次增加的大小
#define NUM_SUBJECT 5 //科目数
struct student_info
{
char number[15]; //学号
char name[20]; //姓名
char gender[4]; //性别
float score[NUM_SUBJECT]; //分别为该学生5门课的成绩
float sum; //总分
float average; //平均分
int index; //名次
};
typedef struct student_info stuinfo;
extern int numstus; //记录的学生数
extern stuinfo*records; //记录学生信息的数组
extern char savetag; //信息是否已保存的标志,0为以保存,1位未保存
extern int arraysize; //数组大小
extern char* subject[];
void handle_menu(void);
int menu_select(void);
void addrecord(void);
void modifyrecord(void)
void display(void);
void queryinfo(void);
void removerecord(void);
void sortinfo(void);
int saverecords(void);
int loadrecords(void);
void newrecords(void);
void quit(void);
void showtable(void);
int findrecord(char*target, int targettype, int from);
int getindex(float sum);
void copyrecord(stuinfo*src, stuinfo*dest);
#endif //H_STUDENT_HH
2.student.c
#include
int numstus=0;
stuinfo *records=null;
char savedtag=0;
int arraysize;
char* subject[]={“语文”,“数学”,“英语”,“物理”,“化学”};
int main()
{
//初始化数组
records=(stuinfo*)malloc(stuinfo)*INITIAL_SIZE);
if (records==null)
{
printf(“memory fail!”);
exit(-1);
}
arraysize = INITIAL_SIZE;
printf(“\n”);
printf(“\t******************************\n”);
printf(“\t* 这是一个*\n”);
printf(“\t* 学生成绩管理程序*\n”);
printf(“\t 可以对学生成绩进行管理*\n”);
printf(“\t 欢迎使用管理程序*\n ”);
printf(“\n”);
handle_menu();
}
void handle_menu(void)
{
for( ; ; )
{
switch(menu_select())
{
case 0:
addrecord();
break;
case 1:
modifyrecord();
break;
case 2:
display();
break;
case 3:
queryinfo();
break;
case 4:
removerecord();
break;
case 5:
sortinfo();
break;
case 6:
saverecords();
break;
case 7:
loadrecords();
break;
case 8:
newrecords();
break;
case 9:
quit();
}
}
}
int menu_select()
{
char s[2];
int cn=0;
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(“\n\t左边数字对应功能选择,请选0-9:”);
for( ; ; )
{
gets(s);
cn=atoi(s);
if(cn==0&&(strcmp(s,”0”)!=0)) cn=11;//处理键入的非数字键,过滤出数字0
if(cn<0||cn>9) printf(“\n\t输出错误,重选0—9:”);
else break;
}
return cn;
}
//新建学生信息记录
void newrecords(void)
{
char str[5];
if(numstus!=0)
{
printf(“现在已经有记录,选择处理已有记录的方法。\n”);
printf(“是否保存原来的记录?(Y/n)”);
gets(str);
if(str[0]!=‘n’&&str[0]!=‘N’)
saverecords();
}
}
numstus=0;
addrecord();
}
//结束运行,退出
void quit(void)
{
char str[5];
if (savedtag==1)
{
printf(“是否保存原来的记录?(Y/n)”);
gets(str);
if(str[0]!=‘n’&&str[0]!=‘N’)
saverecords();
}
free(records);
exit(0);
}
3.add_disp.c
#include
//打印表头
void showtable(void)
{
int j;
printf(“学号\t姓名\t性别”);
for(j=0;j printf(“\t总分\t平均分\t名次\n”); } //显示所有的学生信息 void display(void) { int i,j; if(numstus==0) { printf(“没有可供显示记录!”); return; } shoetable(); for (i=0;i { //打印学生信息 printf((“%s\t%s\t%s”,records[i].number,records[i].name, records[i].gender); for(j=0;j printf(“\t%.1f”,records[i].score[j]); printf(“\t%.1f\t%.1f\t%d\n”,records[i].sum,records[i].average,records[i].index); //打印满20个记录后停下来 if (i%20==0&&i!=0) { printf(“输入任意字符后继续…\n”); getch(); printf(“\n\n”); showtable(); } } } //在当前表的末尾增加新的信息 void addrecord(void) { char str[10]; int j; float mark,sum; if(numstus==0) printf(“原来没有记录,现在建立新表\n”); else printf(“下面在当前表的末尾增加新的信息\n”); while(1) { printf(“您将要添加一组信息,确定吗?(Y/n)”); gets(str); if(str[0]==‘n’||str[0]==‘N’) //不再添加新的信息 break; if(numstus>=arraysize) //数组空间不足,需要重新申请空间 { records=realloc(records,(arraysize+INCR_SIZE)*sizeof(stuinfo)); if(records==NULL) { printf(“memory failed!”); exit(-1); } arraysize=arraysize+INCR_SIZE; } printf(“请输入学号:”); gets(records[numstus].number); printf(“请输入姓名:”); gets(records[numstus].name); printf(“请输入性别(0为女,1为男):”); gets(str); if(str[0]==‘0’) strcpy(records[numstys].gender,‘女’); else strcpy(records[numstys].gender,‘男’); sum=0; for(j=0;j { printf(“请输入%s成绩:”,subject[j]); gets(str); mark=(float)atof(str); records[numstus].score[j]=mark; sum+=mark; } records[numstus].sum=sum; records[numstus].average=sum/NUM_SUBJECT; records[numstus].index=getindex(sum); numstus++; } printf(“现在一共有%d条信息\n”,numstus); savedtag=1; } int i; int count=0; //总分大于sum的人数 for (i=0;i { if (records[i].sum { records[i].index++; //总分小于sum的记录名次增1 } else if (records[i].sum>sum) { count++; } } return count+1 } 4.sav_load.c; #include int saverecords() { FILE *fp; Char fname[30]; If(numstus==0) { printf( “没有记录可存!”); return –1; } printf(“请输入要存入的文件名(直接回车选择文件stus_info):”); gets(fname); if(strlen(fname)==0) strcpy(fname,”stus_info”); if((fp=fopen(fname,”wb”))==NULL) { printf(“不能存入文件!\n”);; return –1; } printf(“\n存文件…\n”); fwrite(records,sizeof(stuinfo)*numstus,1,fp); fclose(fp); printf(“%d条记录已经存入文件,请继续操作。\n”,numstus); savedtag=0; return 0; //更新是否已保存的标志} int loadrecords(void) { FILE *fp; Char fname[30]; Char str[5]; If (numstus!=0&&savedtag==0) { printf(“请选择您是要覆盖现有记录(y),还是要将”); printf(“读取得记录添加到现有记录之后(n)?\n”); printf(“直接回车则覆盖现有记录\n”); gets(str); if(str[0]==‘n’||str[0]==‘N’) { savedtag=1; } else { if ( savedtag==1) { printf(“读取文件将会更改原来的记录,”); printf(“是否保存原来的记录?(Y/n)”); gets(str); if (str[0]!==‘n’&&str[0]!==‘N’) saverecords(); } numstus=0; } } printf(“请输入要读取得文件名(直接回车选择文件stu_info):”); gets(fname); if(strlen(fname)==0) strcpy(fname,”stu_info”); if((fp=fopen(fname,”rb”))==NULL) { printf(“打不开文件!请重新选择\n”); return –1; } printf(“\n取文件…\n”); while(!feof(fp)) { if(numstus>=arraysize) { records=realloc(records,(arraysize+INCR_SIZE)*sizeof(stuinfo)) ; if(records==NULL) { printf(“memory failed!”); exit(-1); } arraysize= arraysize+INCR_SIZE; } if (fread(&records[numstus], sizeof(stuinfo),1,fp)!=1) break; records[numstus].index=getindex(records[numstus].sum);numstus++; } fclose(fp); printf(“现在共有%d条记录。”numstus); return 0; } 5.quee_remv_modi.c #include int findrecord(char*target,int targettype,int from) { int i; for (i=from;i { if((targettype==0&&strcmp(target,records[i].number)==0)|| (targettype==1&&strcmp(target,records[i].number)==0)|| (targettype==2&&atoi(target)==records[i].index)) return i; } return -1; } void queryinfo(void) { char str[5]; char target[20]; int type; int count; int i,j; if(numstus==0) { printf(“没有可供查询的记录!”); return; } while(1) { printf(“请输入查询的方式:(直接输入回车则结束查询)\n”); printf(“1.按学号\n”); printf(“2.按姓名\n”); printf(“3.按名次\n”); gets(str); if(strlen(str)==0) break; if(str[0]==‘1’) { printf(“请输入欲查询的学生的学号:”); gets(target); type=0; } else if (str[0]==‘2’) { printf(“请输入欲查询的学生的姓名:”); gets(target); type=1; } else { printf(“请输入欲查询的学生的名次:”); gets(target); type=2; } i=findrecord(target,type,o); if(i==1) { showtable(); } count =0; while(i!=-1) { count++; printf((“%s\t%s\t%s”,records[i].number,records[i].name, records[i].gender); for(j=0;j printf(“\t%.1f”,records[i].score[j]); printf(“\t%.1f\t%.1f\t%d\n”,records[i].sum,records[i].average,records[i].index); I=findrecord(target,type,i+1); } if (count==0) printf(“没有符合条件的学生!\n”); else printf(“一共找到了%d名学生的信息\n\n”,count); } } void removerecord(void) { char str[5]; char target[20]; int type; int tmpi; int i,j; if(numstus==0) { printf(“没有可供删除的记录!”); return; } while(1) { printf(“请输入如何找到欲删除的记录的方式:”); printf(“(直接输入回车则结束移除操作)\n”); printf(“1.按学号\n”); printf(“2.按姓名\n”); printf(“3.按名次\n”); gets(str); if(strlen(str)==0) break; if(str[0]==‘1’) { printf(“请输入欲查询的学生的学号:”); gets(target); type=0; } else if (str[0]==‘2’) { printf(“请输入欲查询的学生的姓名:”); gets(target); type=1; } else { printf(“请输入欲查询的学生的名次:”); gets(target); type=2; } i=findrecord(target,type,o); if(i==-1) printf(“没有符合条件的学生!\n”); while(i!=-1) { showtable(); printf((“%s\t%s\t%s”,records[i].number,records[i].name, records[i].gender); for(j=0;j printf(“\t%.1f”,records[i].score[j]); printf(“\t%.1f\t%.1f\t%d\n”,records[i].sum,records[i].average,records[i].index); printf(“确定要删除这个学生的的信息吗?(Y/N)”); gets(str); } if(str[0]==‘y’||STR[0]==‘Y’) { numstus--; tmpi=records[i].index; for(j=i;j { copyrecord(&records[j+1],&records[j]); } for(j=0; j { if(records[j].index>tmpi) records[j].index--; } i=findrecord(target,type,i++); } } savedtag=1; } void copyrecord(stuinfo*src,stuinfo*dest) { int j; strcpy(dest->number,src->number); strcpy(dest->name,src->name); strcpy(dest->gender,src->gender); for(j=0;j { dest->score[j]=src->score[j]; } dest->sum=src->sum; dest->average=src->average; dest->index=src->index; void modifyrecord(void) { char str[5]; char target[20]; int type; int tmpi; float sum,mark; int count=0; if(numstus==0) { printf(“没有可供选择的记录!”); return; } while(1) { printf(“请输入如何找到欲修改的记录的方式:”); printf(“(直接输入回车则结束移除操作)\n”); printf(“1.按学号\n”); printf(“2.按姓名\n”); printf(“3.按名次\n”); gets(str); if(strlen(str)==0) break; if(str[0]==‘1’) { printf(“请输入欲查询的学生的学号:”); gets(target); type=0; } else if (str[0]==‘2’) { printf(“请输入欲查询的学生的姓名:”); gets(target); type=1; } else { printf(“请输入欲查询的学生的名次:”); gets(target); type=2; } i=findrecord(target,type,o); if(i==-1) printf(“没有符合条件的学生!\n”); while(i!=-1) { showtable(); printf((“%s\t%s\t%s”,records[i].number,records[i].name, records[i].gender); for(j=0;j printf(“\t%.1f”,records[i].score[j]); printf(“\t%.1f\t%.1f\t%d\n”,records[i].sum,records[i].average,records[i].index); printf(“确定要修改这个学生的的信息吗?(Y/N)”); gets(str) if(str[0]==‘y’||STR[0]==‘Y’) { tmpi=records[i].index; printf(“下面请重新输入该学生的信息:\n”); printf(“请输入学号:”); gets(records[numstus].number); printf(“请输入姓名:”); gets(records[numstus].name); printf(“请输入性别(0为女,1为男):”); gets(str); if(str[0]==‘0’) strcpy(records[numstys].gender,‘女’); else strcpy(records[numstys].gender,‘男’); sum=0; for(j=0;j { printf(“请输入%s成绩:”,subject[j]); gets(str); mark=(float)atof(str); records[i].score[i]=mark; sum+=mark; } records[i].sum=sum; records[i].average=sum/NUM_SUBJECT; count=0; for(j=0;j { if(j==I)continue; if(records[j].index>tmpi&&records[j].sum>sum) records[j].index--; elseif(records[j].index<=tmpi&&records[j].sum records[j].index++; if(records[j].sum>sum) count++; } records[j].index=count+1; } i=findrecord(target,type,i++); } } savedtag=1; } 6.sort.h #include