1.实验要求
老师在教学过程中,会经常以试卷的形式来检验学生的学习情况。现在由你来帮助老师设计一个试卷自动生成系统,完成从已有题库(题库包含2个文件,1个是选择题题库文件,1个是填空题题库文件)中随机提取指定题目书的题目生成一份新的试卷。该系统生成的试卷中只有2种题型:单项选择题、填空题(只有一个空)。其中
单项选择题包括题目编号、题目、选项A、选项B、选项C、选项D、答案
填空题包括题目编号、题目、答案
功能:
(1)试题添加:向试题库追加写入一道新的题目,要求题目编号自动生成,且与已存题目的编号不重复;所有内容不能为空。即不断充实题库;
(2)试题删除:通过题目编号进行题目的删除,如果删除成功则提示删除成功,否则提示删除失败;
(3)备份全部题目;
(4)删除全部题目;
(5)试题修改:通过题目编号查找对应题目,并修改指定的题目的内容,注意不是修改题目的全部内容,而是可以针对性的修改局部内容;
(6)试题查询:通过题目编号查询指定的题目的所有内容;
(7)统计共有多少道题目;
(8)查询题目中含有某个特定内容(用户输入)的所有题目内容;
(9)自动随机生成由单项选择题、填空题合在一起的试卷及标准答案2个文件(exam.txt和answer.txt),各题型的题目数由用户输入。注意选择题和填空题的题目数可以不一样;要求每次生成的试卷题目、顺序都不能完全相同。也就是要求真正的随机提取题目。
(10)以上功能要求通过菜单的方式进行操作;要求对相应内容进行必要的合法性检查。本题要求用链表实现。要求具有良好的人机交互界面。每个题型要提前录入至少10道题。
2.代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct question1 { //选择题链表
int n;
char topic[1000]; //题目
char A[1000]; //选项
char B[1000];
char C[1000];
char D[1000];
char answer; //答案
struct question1 *next;
} q1;
typedef struct question2 { //填空题链表
int n;
char topic[1000]; //题目
char answer[1000]; //答案
struct question2 *next;
} q2;
int a=0;
///函数清单
void link_free(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2);//链表释放
void read(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2);//将链表内容读到文件
void copy ( FILE *fp1,FILE *fp2 );//将一个文件的内容复制到另一个文件
void myrand(int s1[],int s2[],int num,int n);//从数组s1中随机选取n个数储存到数组s2
void add();//1.试题添加
void mydelete(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2);//2.试题删除
void backups();//3.备份全部题目
void clear();//4.删除全部题目
void modify(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2);//5.试题修改
void find(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2);//6.试题查询
void total_number(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2);//7.统计题目总数
void search(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2);//8.查询含有特定内容的题目内容
void testpaper(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2);//9.随机生成试卷及答案
int main()
{
srand(time(0));
q1 *h1,*t1,*p1;
q2 *h2,*t2,*p2;
FILE *fp1 = fopen("D:\\choice.txt","a+"); //只读的方式打开文件
FILE *fp2 = fopen("D:\\gap_filling.txt","a+");
h1=NULL,h2=NULL;
t1=(q1*)malloc(sizeof(q1)),t2=(q2*)malloc(sizeof(q2));
//将文件内容读入链表
while(fscanf(fp1,"%d\n",&a)!=EOF){
p1=(q1*)malloc(sizeof(q1));
if(h1==NULL) h1=p1;
else t1->next=p1;
p1->next=NULL;
fscanf(fp1,"%s\n",p1->topic);
fscanf(fp1,"%s\n",p1->A);
fscanf(fp1,"%s\n",p1->B);
fscanf(fp1,"%s\n",p1->C);
fscanf(fp1,"%s\n",p1->D);
fscanf(fp1,"%c\n",&p1->answer);
p1->n=a;
t1=p1;
}
while(fscanf(fp2,"%d\n",&a)!=EOF){
p2=(q2*)malloc(sizeof(q2));
if(h2==NULL) h2=p2;
else t2->next=p2;
p2->next=NULL;
fscanf(fp2,"%s\n",p2->topic);
fscanf(fp2,"%s\n",p2->answer);
p2->n=a;
t2=p2;
}
fclose(fp1);
fclose(fp2);
int ch;
printf("\n\n\t\t欢迎进入试卷管理系统");
printf("\n\n\t菜单:\n");
printf("\n\t试题添加-------------------------------------1");
printf("\n\t试题删除-------------------------------------2");
printf("\n\t备份全部题目---------------------------------3");
printf("\n\t删除全部题目---------------------------------4");
printf("\n\t试题修改-------------------------------------5");
printf("\n\t试题查询-------------------------------------6");
printf("\n\t统计共有多少道题目---------------------------7");
printf("\n\t查询题目中含有某个特定内容的所有题目内容-----8");
printf("\n\t自动随机生成试卷及标准答案2个文件------------9");
printf("\n\n\t请选择需要进行的操作:");
re:
scanf("%d",&ch);
switch(ch)
{
case 1:
add();
break;
case 2:
mydelete(h1,t1,p1,h2,t2,p2);
break;
case 3:
backups();
break;
case 4:
clear();
break;
case 5:
modify(h1,t1,p1,h2,t2,p2);
break;
case 6:
find(h1,t1,p1,h2,t2,p2);
break;
case 7:
total_number(h1,t1,p1,h2,t2,p2);
break;
case 8:
search(h1,t1,p1,h2,t2,p2);
break;
case 9:
testpaper(h1,t1,p1,h2,t2,p2);
break;
default:
printf("输入错误,请重新输入");
goto re;
}
link_free(h1,t1,p1,h2,t2,p2);
return 0;
}
///1.试题添加 +
void add(){
system("cls");
FILE* fp;
printf("\n\n\t\t添加选择题 1");
printf("\n\t\t添加填空题 2");
printf("\n\n\t请选择需要添加的题目种类:");
int op1;
re1:
scanf("%d",&op1);
getchar();
if( op1 == 1 || op1 == 2 )
{
if(op1==1) fp = fopen("D:\\choice.txt","a+"); //追加的方式打开文件
else fp = fopen("D:\\gap_filling.txt","a+");
a++;
printf("\n\t新增题目的题号是%d\n",a);
fprintf(fp,"%d\n",a);
printf("\n\t请输入新题目内容\n");
if( op1 == 1 ) printf("\n\t请分别输入添加的题目、选项、答案\n");
else if( op1 == 2 ) printf("\n\t请分别输入添加的题目、答案\n");
char s[10000];
printf("\n\t输入题目:");
gets(s);
fprintf(fp,"%s\n",s);
if( op1 == 1 ) {
printf("\n\t输入选项A:");
gets(s);fprintf(fp,"%s\n",s);
printf("\n\t输入选项B:");
gets(s);fprintf(fp,"%s\n",s);
printf("\n\t输入选项C:");
gets(s);fprintf(fp,"%s\n",s);
printf("\n\t输入选项D:");
gets(s);fprintf(fp,"%s\n",s);
}
printf("\n\t输入答案:");
gets(s);fprintf(fp,"%s\n",s);
printf("\n\t题目添加完毕\n");
}
else {
printf("\n\t输入错误,请重新输入:");
goto re1;
}
fclose(fp);
}
///2.试题删除 +
void mydelete(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2) {
int num;
int flag=0;
printf("\n\t请输入待删除题目的编号:");
scanf("%d",&num);
p1=h1,p2=h2; //遍历结点
if(h1!=NULL){
if(h1->n==num) {
h1=h1->next; //删除结点
flag=1; //标记,存在该题目
}
else{
while(p1->next!=NULL) {
if(p1->next->n==num) {
p1->next=p1->next->next;
flag=1;
break;
}
p1=p1->next;
}
}
}
if(h2!=NULL&&!flag){
if(h2->n==num) {
h2=h2->next;
flag=1;
}
else{
while(p2->next!=NULL) {
if(p2->next->n==num) {
p2->next=p2->next->next;
flag=1;
break;
}
p2=p2->next;
}
}
}
read(h1,t1,p1,h2,t2,p2); //重写文件
if (flag) {
printf("\n\t删除成功!\n");
} else {
printf("\n\t删除失败,不存在该题目!\n");
}
}
///3.备份全部题目+
void backups(){
system("cls");
FILE *fp1, *fp2, *fp3, *fp4;
fp1 = fopen("D:\\choice.txt","r"); //只读
fp2 = fopen("D:\\gap_filling.txt","r");
fp3 = fopen("D:\\choice-backups.txt","w"); //重写
fp4 = fopen("D:\\gap_filling-backups.txt","w");
copy( fp1,fp3 ); //一个文件的内容复制到另一个文件
copy( fp2,fp4 );
printf("\n\t备份成功!");
}
///4.删除全部题目 +
void clear(){
system("cls");
remove("D:\\choice.txt");
remove("D:\\gap_filling.txt");
printf("\n\t删除成功!");
}
///5.试题修改 +
void modify(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2) {
system("cls");
int num;
int flag=0;
printf("\n\t请输入待修改题目的编号:");
scanf("%d",&num);
p1=h1,p2=h2; // 查询原题目内容并打印
if(h1!=NULL)
{
if(h1->n==num) {
printf("\n原题为:\n");
printf("%d\n",p1->n);
printf("%s\n",p1->topic);
printf("%s\n",p1->A);
printf("%s\n",p1->B);
printf("%s\n",p1->C);
printf("%s\n",p1->D);
printf("%c\n",p1->answer);
flag=1;
}
else{
while(p1->next!=NULL) {
if(p1->next->n==num) {
printf("\n原题为:\n");
printf("%d\n",p1->next->n);
printf("%s\n",p1->next->topic);
printf("%s\n",p1->next->A);
printf("%s\n",p1->next->B);
printf("%s\n",p1->next->C);
printf("%s\n",p1->next->D);
printf("%c\n",p1->next->answer);
flag=1;
break;
}
p1=p1->next;
}
}
}
if(h2!=NULL&&!flag)
{
if(h2->n==num) {
printf("\n原题为:\n");
printf("%d\n",p2->n);
printf("%s\n",p2->topic);
printf("%s\n",p2->answer);
flag=2;
}
else{
while(p2->next!=NULL) {
if(p2->next->n==num) {
printf("\n原题为:\n");
printf("%d\n",p2->next->n);
printf("%s\n",p2->next->topic);
printf("%s\n",p2->next->answer);
flag=2;
break;
}
p2=p2->next;
}
}
}
int op5;
char ch;
if(flag){ //存在该题目
if( flag == 1 ) { //该题目为选择题
re1:
printf("\n\t请输入需要修改的内容(题目——1,选项——2,答案——3):");
scanf("%d",&op5);
switch(op5)
{
case 1:
printf("\n请输入修改后的题目内容:");
if( h1->n == num ) scanf("%s",h1->topic);
else scanf("%s",p1->next->topic);
break;
case 2:
re2:
printf("\n请输入要修改的选项(A、B、C、D):");
getchar();
scanf("%c",&ch);
if( ch == 'A' || ch == 'B' || ch == 'C' || ch == 'D' )
{
printf("\n请输入修改后的选项(例:A.***):");
if( ch == 'A' )
{
if( h1->n == num ) scanf("%s",h1->A);
else scanf("%s",p1->next->A);
}
else if( ch == 'B' )
{
if( h1->n == num ) scanf("%s",h1->B);
else scanf("%s",p1->next->B);
}
else if( ch == 'C' )
{
if( h1->n == num ) scanf("%s",h1->C);
else scanf("%s",p1->next->C);
}
else
{
if( h1->n == num ) scanf("%s",h1->D);
else scanf("%s",p1->next->D);
}
}
else
{
printf("\n\t输入错误,请重新输入!");
goto re2;
}
break;
case 3:
printf("\n请输入修改后的答案:");
getchar();
if( h1->n == num ) scanf("%c",&h1->answer);
else scanf("%c",&p1->next->answer);
break;
default:
printf("\n\t输入错误,请重新输入!");
goto re1;
break;
}
}
else{ //该题目为填空题
re3:
printf("\n\t请输入需要修改的内容(题目——1,答案——2):");
scanf("%d",&op5);
switch(op5)
{
case 1:
printf("\n请输入修改后的题目内容:");
if( h2->n == num ) scanf("%s",h2->topic);
else scanf("%s",p2->next->topic);
break;
case 2:
printf("\n请输入修改后的答案:");
if( h2->n == num ) scanf("%s",h2->answer);
else scanf("%s",p2->next->answer);
break;
default:
printf("\n\t输入错误,请重新输入!");
goto re3;
break;
}
}
read(h1,t1,p1,h2,t2,p2);
printf("\n\t修改成功!");
}
else {
printf("\n\t修改失败,不存在该题目!");
}
}
///6.试题查询 +
void find(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2) {
system("cls");
int flag=0;
int num;
p1=h1,p2=h2;
printf("\t请输入查询的题目编号:");
scanf("%d",&num);
if(h1!=NULL)
{
if(h1->n==num) {
printf("%d\n",p1->n);
printf("%s\n",p1->topic);
printf("%s\n",p1->A);
printf("%s\n",p1->B);
printf("%s\n",p1->C);
printf("%s\n",p1->D);
printf("%c\n",p1->answer);
flag=1;
}
else{
while(p1->next!=NULL) {
if(p1->next->n==num) {
printf("%d\n",p1->next->n);
printf("%s\n",p1->next->topic);
printf("%s\n",p1->next->A);
printf("%s\n",p1->next->B);
printf("%s\n",p1->next->C);
printf("%s\n",p1->next->D);
printf("%c\n",p1->next->answer);
flag=1;
break;
}
p1=p1->next;
}
}
}
if(h2!=NULL&&!flag)
{
if(h2->n==num) {
printf("%d\n",p2->n);
printf("%s\n",p2->topic);
printf("%s\n",p2->answer);
flag=1;
}
else{
while(p2->next!=NULL) {
if(p2->next->n==num) {
printf("%d\n",p2->next->n);
printf("%s\n",p2->next->topic);
printf("%s\n",p2->next->answer);
flag=1;
break;
}
p2=p2->next;
}
}
}
if (flag) {
printf("\n查询成功!\n");
} else {
printf("\n查询失败,不存在该题目!\n");
}
}
///7.统计题目总数 +
void total_number(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2) {
int num1=0,num2=0;
p1=h1,p2=h2;
while(p1!=NULL) //求结点总数
{
num1++;
p1=p1->next;
}
while(p2!=NULL)
{
num2++;
p2=p2->next;
}
printf("\n\t选择题题目总数为:%d",num1);
printf("\n\t填空题题目总数为:%d",num2);
printf("\n\t所有题目总数为:%d",num1+num2);
}
///8.查询含有特定内容的题目内容 +
void search(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2) {
system("cls");
p1=h1,p2=h2;
int flag=0;
printf("\n\t请输入查找的内容:");
char s[1000];
scanf("%s",s);
while(p1!=NULL)
{
if( strstr(p1->topic,s)!=NULL || //查询字符串s是否是该题子串
strstr(p1->A,s)!=NULL ||
strstr(p1->B,s)!=NULL ||
strstr(p1->C,s)!=NULL ||
strstr(p1->D,s)!=NULL )
{
flag=1;
printf("%d\n",p1->n); //打印
printf("%s\n",p1->topic);
printf("%s\n",p1->A);
printf("%s\n",p1->B);
printf("%s\n",p1->C);
printf("%s\n",p1->D);
printf("%c\n",p1->answer);
}
p1 = p1->next;
}
while(p2!=NULL)
{
if( strstr(p2->topic,s)!=NULL )
{
flag=1;
printf("%d\n",p2->n);
printf("%s\n",p2->topic);
printf("%s\n",p2->answer);
}
p2 = p2->next;
}
if(flag) printf("\n\t查询成功!");
else printf("\n\t查询失败,不存在该内容!");
}
///9.随机生成试卷及答案 +
void testpaper(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2) {
system("cls");
FILE *fp1 = fopen("D:\\exam.txt","w"); //重写
FILE *fp2 = fopen("D:\\answer.txt","w");
int num1=0,num2=0; //统计题目数目
p1=h1,p2=h2;
int s1[1000],s2[1000],s3[1000],s4[1000];
while(p1!=NULL)
{
s1[num1] = p1->n; //将题目编号储存到数组
num1++;
p1=p1->next;
}
while(p2!=NULL)
{
s2[num2] = p2->n;
num2++;
p2=p2->next;
}
printf("\n\t选择题题目总数为:%d",num1);
printf("\n\t填空题题目总数为:%d",num2);
int n1,n2; //输入题目数目
re1:
printf("\n\n\t请输入选择题题目数目:");
scanf("%d",&n1);
if(n1>num1){
printf("\n\t超过题目总数,请重新输入!");
goto re1;
}
re2:
printf("\n\t请输入填空题题目数目:");
scanf("%d",&n2);
if(n1>num1){
printf("\n\t超过题目总数,请重新输入!");
goto re2;
}
myrand(s1,s3,num1,n1); //随机选择题目编号
myrand(s2,s4,num2,n2);
int i,k=1;
for(i=0;i<n1;i++) //选择题目
{
p1=h1;
while(p1->n!=s3[i]){
p1 = p1->next;
}
if(p1->n==s3[i])
{
fprintf(fp1,"%d\n",k); //写入文件
fprintf(fp1,"%s\n",p1->topic);
fprintf(fp1,"%s\n",p1->A);
fprintf(fp1,"%s\n",p1->B);
fprintf(fp1,"%s\n",p1->C);
fprintf(fp1,"%s\n",p1->D);
fprintf(fp2,"%d\n",k);
fprintf(fp2,"%c\n",p1->answer);
k++;
}
}
for(i=0;i<n2;i++) //填空题目
{
p2=h2;
while(p2->n!=s4[i]){
p2 = p2->next;
}
if(p2->n==s4[i])
{
fprintf(fp1,"%d\n",k);
fprintf(fp1,"%s\n",p2->topic);
fprintf(fp2,"%d\n",k);
fprintf(fp2,"%s\n",p2->answer);
k++;
}
}
printf("\n\t试卷生成成功!");
fclose(fp1);
fclose(fp2);
}
///将链表内容读到文件 +
void read(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2) {
FILE *fp1 = fopen("D:\\choice.txt","w"); //重写文件
FILE *fp2 = fopen("D:\\gap_filling.txt","w");
p1=h1,p2=h2;
while( p1 != NULL )
{
fprintf(fp1,"%d\n",p1->n);
fprintf(fp1,"%s\n",p1->topic);
fprintf(fp1,"%s\n",p1->A);
fprintf(fp1,"%s\n",p1->B);
fprintf(fp1,"%s\n",p1->C);
fprintf(fp1,"%s\n",p1->D);
fprintf(fp1,"%c\n",p1->answer);
p1 = p1->next;
}
while( p2 != NULL )
{
fprintf(fp2,"%d\n",p2->n);
fprintf(fp2,"%s\n",p2->topic);
fprintf(fp2,"%s\n",p2->answer);
p2 = p2->next;
}
fclose(fp1);
fclose(fp2);
}
///将一个文件的内容复制到另一个文件 +
void copy ( FILE *fp1,FILE *fp2 )
{
if( fp1 == NULL) return ;
char ch;
ch = fgetc(fp1);
while( ch != EOF)
{
fprintf( fp2,"%c",ch );
ch = fgetc(fp1);
}
}
///从数组s1中随机选取n个数储存到数组s2 +
void myrand(int s1[],int s2[],int num,int n){
int i;
for( i=0;i<n;i++)
{
int t=rand()%num;
if(s1[t])
{
s2[i] = s1[t];
s1[t] = -1;
}
else i--;
}
}
///链表释放
void link_free(q1 *h1,q1 *t1,q1 *p1,q2 *h2,q2 *t2,q2 *p2){
while(h1!=NULL)
{
p1=h1;
free(p1);
p1=NULL;
if(h1->next==NULL) break;
h1=h1->next;
}
while(h2!=NULL)
{
p2=h2;
free(p2);
p2=NULL;
if(h2->next==NULL) break;
h2=h2->next;
}
}
3.代码讲解
该实验主要考察链表和文件的应用。
我建立了两个链表,选择题链表和填空题链表。
主函数首先将文件内容读取到链表,然后进入菜单,选择相应的操作。
1试题添加,就是向文件末尾添加题目。
2试题删除,就是删除节点,然后再将列表内容读取到文件。
3备份题目,就是新建两个文件,将原文件内容读取到新文件。
4删除全部题目,就是将文件删除。
5试题修改,就是按输入的题号找到该题目,然后修改对应的数据。
6试题查询,就是链表查询。
7统计题目总数,就是统计结点数。
8查询含有特定内容的题目内容,就是遍历链表,含有指定内容就输出,这里我用了strstr函数,查找题目中是否有s这个字符串。
9随机生成试卷及答案,我首先输出题目数量,用户可以根据题目数量,选择生成试卷的题目数量,mysand函数是从s1中随机选择n1个数储存在s3,这就相当于从所有的题目中随机抽题,然后遍历链表,输出对应的题目内容。