这个程序是我大一上学期的期末课程设计,在网上查了许多的资料,用了5天左右的时间和同学一起完成的。因为我们才刚入门c语言,所以程序可能有点拙劣,但还是希望能帮助各位同学。
//所用的IDE是VC++6.0
//所录入的题目是思政课的选择题
(1)用文件保存试题库。(每个试题包括题干、4个备选答案、标准答案)
(2)试题录入:可随时增加试题到试题库中
(3)试题抽取:每次从试题库中可以随机抽出N道题(N由键盘输入)
(4)答题:用户可实现输入自己的答案
(5)自动判卷:系统可根据用户答案与标准答案的对比实现判卷并给出成绩。
1.结构组与函数
typedef struct
{
char question[200],A[100],B[100],C[100],D[100];
char key;
}Question;//结构组,结构类型Question
void Menu();//主菜单
void Remenu();//返回菜单或退出
void Answer();//抽取答题
void Add();//试题录入
void Deleteq();//试题删除
void Readfile();//读取所有题目
void Putques1();//输出问题1(不输出答案)
void Putques2();//输出问题2(输出答案)
int Total();//计算总题目数量
抽取题目首先需要读取题目。我们的设计思路是建立一个结构数组用来存储题目信息,其中包括题目,ABCD四个选项以及答案,结构如下:
typedef struct
{
char question[200],A[100],B[100],C[100],D[100];
char key;
}Question;//结构组,结构类型Question
起初我们准备直接建立一个固定的结构数组Question qs[100]用来在内存中存储题目,但发现这样有许多弊病。
①容易造成内存空间的浪费
②读取时容易造成越界(“烫烫烫烫烫烫……”)
③存在题目上限,不方便后续添加题目
于是我们决定利用malloc()进行动态分配内存,生成动态结构数组。但是进行动态分配内存需要实现知道题目数量。于是我们做了int Total()函数来预读一遍题目数量,然后将题目总数返回给total,利用total来生成动态结构数组。这样就解决了上面三个问题,但是伴随而来的问题是题目读取时间变长了。
2.菜单的实现与返回
//主菜单函数:主要用于用户使用时功能的选择,包括了Answer(),Add(),Deleteq(),exit(0)函数
void Menu()
{
int n;//记录序列号
system("cls");//清屏
printf("************************************************************************************************************************\n");
printf("* 单项选择题标准化考试系统 V2.4 *\n");
printf("* *\n");
printf("* ①抽取答题 *\n");
printf("* *\n");
printf("* ②试题录入 *\n");
printf("* *\n");
printf("* ③试题删除 *\n");
printf("* *\n");
printf("* ④ 退出 *\n");
printf("************************************************************************************************************************\n");//美化菜单
do
{
printf(" ┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉\n");
printf(" 请输入正确的序列号:\n");
printf(" ┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉\n");
scanf("%d",&n);
printf("\n");
}while(n<1||n>4);//若输入序列号不正确,返回循环体,重新输入,否则跳出循环
switch(n)//通过switch-case对序列号对应的要求实现
{
case 1:
Answer();//抽取答题
break;
case 2:
Add();//试题录入
break;
case 3:
Deleteq();//试题删除
break;
case 4:
exit(0);//退出
break;
default:
printf("错误\n");//default语句在此其实并无实际用处
}
}
3.用文件保存试题库与试题录入
//题目录入函数():录入题目。包含Remenu()函数
void Add()
{
FILE* readf;
char ch;
system("cls");//清屏
printf("************************************************************************************************************************\n");
printf(" 试题录入 \n");
printf("************************************************************************************************************************\n");
readf=fopen("question.txt","a");//a表示对文件追加内容
if(!readf)
{
printf("打开文件失败\n");
exit(1);
}
printf("请按照以下格式分别录入问题、ABCD四个选项及正确答案并以#结束:\n");//可以自由录入题目数量,停止录入只需在末尾加#
printf("例:步入人生新阶段,确立新目标,开启新征程,需要对()有深入的了解和真切的感悟。\n");//用户友好
printf("社会\n");
printf("个人\n");
printf("新时代\n");
printf("大学\n");
printf("C\n");
ch=getchar();//读取第一个字符
while(ch!='#')
{
fputc(ch,readf);//将读入的字符写入文件
ch=getchar();//读取下一个字符
}
fclose(readf);
Remenu();//返回菜单或退出
}
4.试题抽取、答题与自动判卷
考虑到程序的实用性,我们决定将试题抽取、答题与自动判卷放到一起,实现“抽取答题”的功能。用户进入抽取答题的页面后,只需要输入想要答题的数目即可进行答题。为了让答题方便,我们采用答一题输入下一题的形式,最终会生成总成绩让用户选择是返回主菜单还是退出程序。
//抽取答题函数:用于实现抽取,答题,自动批阅的功能。其中包含了Readfile(),Putques1(),Total(),Remenu()这些功能函数
void Answer()
{
int i,j,total,n;//i用于索引,j也用于索引同i一起实现生产不重复的随机数组的功能。total用来存储总题目数。n用来存储用户输入的抽取题目数目。
int *a;//生产整型指针,用于建立动态数组
Question *qs;//生成Question型指针,用于建立动态结构数组
int score=0;//用于记分
char an;//存储用户输入的答案
total=Total();//利用Total()函数将总题目数返回给total
qs=(Question*)malloc(total*sizeof(Question));//分配空间给动态结构数组
Readfile(qs,total);//读取题目
system("cls");//清屏
printf("************************************************************************************************************************\n");//美化界面
printf(" 抽取答题 \n");
printf("************************************************************************************************************************\n");
printf("当前题库题目数:%d\n",total);
loop1:printf("请输入抽查题目数量:");//loop1充当前哨,防止用户输入的n>total
scanf("%d",&n);
printf("正在拼命加载中……\n");
printf("………………………………………………………………………………………………………………………………………………………………\n");
if(n<=total)
{//开始生成不重复的随机数组
a=(int*)malloc(n*sizeof(int));//分配空间生成动态数组
for(i=0;i<n;i++)
{
loop2:srand(time(NULL));//srand和rand一起使用,这条语句大家把它看作用rand之前的一个习惯用法(rand为随机数)
a[i]=rand()%total;//保证随机数在total内(小于total)(total为总题数)
for(j=i-1;j>=0;j--)
{
if(a[i]==a[j])
goto loop2;//如果新抽出的题目(a[i])与已抽出的题目(a[j])一样,跳到loop:后的语句重新抽取a[i]
}
}
}
else
{
printf("超过最大题数,最大题数为%d\n",total);//保证抽出的题目不重复且在系统支持范围内(题目数量不超出总题数)
goto loop1;
}
for(i=0;i<n;i++)//利用循环输出所抽到的问题
{
Putques1(&qs[a[i]],i+1);
fflush(stdin);//清空键盘缓冲区
an=getchar();//读取用户输入的答案
while((int)an<65||((int)an>68&&(int)an<97)||(int)an>100)//用强制转换判断用户输入的是否为选项
{
printf("请输入A,B,C,D其中之一作为你的答案\n");
fflush(stdin);//清空键盘缓冲区
an=getchar();//读取用户输入的答案
}
if((int)an>96&&(int)an<101)//若用户输入的是小写字母,让其转换为大写字母
an=toupper(an);
if(qs[a[i]].key==an)//批改
{
score+=1;//正确+1分
printf("正确\n\n");
}
else
printf("错误\n正确答案为%c\n\n",qs[a[i]].key);
}
printf("¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤\n");
printf("共%d题,您答对了%d道题\n",n,score);
printf("得分:%f\n",((float)score/n)*100);//评分
printf("¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤\n");
free(qs);//释放分配的内存空间
free(a);//同上
Remenu();//返回菜单或退出
}
5.试题删除
介于时间比较充足,又考虑到本程序功能的完整性,我们又添加了一个试题删除功能。
//试题删除函数():用于删除题目。包含Total(),Readfile(),Putques2(),Remenu()函数
void Deleteq()
{
int i,j;
char o;
Question qs[5];
FILE *rewritef,*readf;
readf=fopen("question.txt","r");
if(!readf)
{
printf("打开文件失败\n");
exit(1);
}
rewritef=fopen("temp.txt","w+");
if(!readf)
{
printf("打开文件失败\n");
exit(1);
}
while(!feof(readf))
{
system("cls");
printf("************************************************************************************************************************\n");
printf(" 试题删除 \n");
printf("************************************************************************************************************************\n");
for(i=0;(feof(readf)==0)&&i<5;i++)
fscanf(readf,"%s%s%s%s%s%c%c",qs[i].question,qs[i].A,qs[i].B,qs[i].C,qs[i].D,&o,&qs[i].key);
for(j=0;j<i;j++)
Putques2(&qs[j],j+1);
printf("输入题目序号进行删除,输入0进入下一页。\n");
scanf("%d",&j);
while(j>i)
{
printf("输入题目序号进行删除,输入0进入下一页。\n");
scanf("%d",&j);
}
if(j>0&&j<i+1)
{
while(j!=0)
{
for(j--;j<i-2;j++)//利用循环,覆盖删除
qs[j]=qs[j+1];
i--;
system("cls");
for(j=0;j<i;j++)
Putques2(&qs[j],j+1);
printf("输入题目序号进行删除,输入0进入下一页。\n");
scanf("%d",&j);
while(j>i)
{
printf("输入题目序号进行删除,输入0进入下一页。\n");
scanf("%d",&j);
}
}
}
for(j=0;j<i;j++)//输出删除后所有题目
fprintf(rewritef,"%s\n%s\n%s\n%s\n%s\n%c\n",qs[j].question,qs[j].A,qs[j].B,qs[j].C,qs[j].D,qs[j].key);
}
fclose(rewritef);
fclose(readf);
remove("question.txt");
rename("temp.txt","question.txt");
Remenu();//返回菜单或退出
}
从主菜单输入3进入试题删除界面,输入题号可直接删除
输入1继续删除,输入其他数字退出
6.其它函数
//返回菜单/退出函数:用于完成一项功能后让用户选择是返回菜单还是退出应用程序
void Remenu()
{
int n;//存储用户输入的数字(通过数字发出指令)
do//前哨
{
printf("§返回主菜单§请输入①\n");
printf("§ 退出 §请输入②\n");
scanf("%d",&n);
}while(n<1||n>2);//若为1或2跳出循环,否则执行循环体要求用户重新输入
if(n==1)
Menu();//n=1,返回主菜单
else
exit(0);//n=2,退出
}
//读题函数():用于读取文件内所有题目并存在内存空间中。
void Readfile(Question* qs,int total)//导入Question型指针,以及总题目数量total
{
int i=0;//作用1:结构数组索引;作用2:其最终值刚好为总题数值,返回i给Menu()函数里的total,使得total为总题数值
char o;//用于读取回车符,防止题目信息读取错位
FILE* readf;
readf=fopen("question.txt","r");//仅用读的方式打开文件
if(!readf)
{
printf("打开文件失败\n");
exit(1);
}
for(i=0;i<total;i++)//利用循环按顺序将所有题目存在内存空间中
fscanf(readf,"%s%s%s%s%s%c%c",qs[i].question,qs[i].A,qs[i].B,qs[i].C,qs[i].D,&o,&qs[i].key);
fclose(readf);//关闭文件,结束该函数
}
//计算总题目数函数():功能入其名,利用几个空字符串读取文件内所有题目,读完一题total加一次1。
int Total()
{
int total;//total用于存储总题目数
char question[200],A[100],B[100],C[100],D[100];//建立空字符串用于存储读的题目
char key,o;
FILE* readf;
readf=fopen("question.txt","r");
if(!readf)
{
printf("打开文件失败\n");
exit(1);
}
for(total=0;!feof(readf);total++)//每读完一个题目,总题目数+1
fscanf(readf,"%s%s%s%s%s%c%c",question,A,B,C,D,&o,&key);
total-=1;
fclose(readf);//关闭文件
return total;//返回total的值并退出该函数
}
//输出题目函数():用于题目的打印。一个打印出来无答案,一个有答案。分别用于抽取答题函数和试题删除函数。
void Putques1(Question* qs,int i)//导入Question型指针,以及当前题目序号i
{printf("(%d)%s\n A.%s\n B.%s\n C.%s\n D.%s\n",i,qs->question,qs->A,qs->B,qs->C,qs->D);}
void Putques2(Question* qs,int i)
{printf("(%d)%s\n A.%s\n B.%s\n C.%s\n D.%s\n 答案:%c\n",i,qs->question,qs->A,qs->B,qs->C,qs->D,qs->key);}
相关报告书和代码文件在我上传的资源里,需要的可以进我的主页去下载。