实现一个自动生成小学四则运算题目的命令行程序

C语言实现简单自动生成小学四则运算题目

作者:邱彬

协作者:麦狄龙(麦狄龙指导我指导他为指导我的指导人)

1.GitHub项目网址:

 https://github.com/QiuBin666/MyApp.exe

2.个人PSP

PSP(严格来说是基于团队的PSP)

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning 计划 10  
· Estimate · 估计这个任务需要多少时间 20  
Development 开发 1200  
· Analysis · 需求分析 (包括学习新技术) 180  
· Design Spec · 生成设计文档 60  
· Design Review · 设计复审 (和同事审核设计文档) 20  
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 60  
· Design · 具体设计 120  
· Coding · 具体编码 360  
· Code Review · 代码复审 60  
· Test · 测试(自我测试,修改代码,提交修改) 180  
Reporting 报告 60  
· Test Report · 测试报告 60  
· Size Measurement · 计算工作量 10  
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30  
合计   2430  

3.题目介绍与解题思路

3.1题目介绍:

  1. 使用 -n 参数控制生成题目的个数,例如:Myapp.exe -n 10 将生成10个题目。

  2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如 Myapp.exe -r 10 ,将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

  3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。

  4. 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数

  5. 每道题目中出现的运算符个数不超过3个。

  6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。

  7. 生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:

    四则运算题目1

    四则运算题目2

    ……

    其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。

    在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:

    1.答案1

    2.答案2

    特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

  8. 程序应能支持一万道题目的生成。

  9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:

    Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt

    统计结果输出到文件Grade.txt,格式如下:

    Correct: 5 (1, 3, 5, 7, 9)

    Wrong: 5 (2, 4, 6, 8, 10)

    其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

3.2解题思路:

  1. 生成表达式:表达式由随机数和运算符两部分组成,运算符生成运用rand函数来随机生成一个1到4的变量,分别对应加号,减号,乘号,除号,当运算符为除号的时候我们采用了辗转相除法,写一个gcd函数来求出两个数的最大公约数,这样来实现输出分数的要求;随机数生成也运用rand函数来实现。

  2. 生成题目文件和答案文件:先定义两个文件指针,指向相对应要写入的文件Answer.txt和Exercise.txt,然后生成文件并打开文件,通过fprinft函数将生成的题目和答案写入到相应的文件。

        3.  判定答案中的对错并进行数量统计:只需对前面生成的题目文件和答案文件进行操作,分三步:

            (1).计算题目的数量x:遍历Exercise.txt,遇到换行符x加1。

    (2).分别读取Answer.txt和Exercise.txt的答案:读取等号右边的数据。

    (3).判断第二步读取的两个答案是否相等,记录序号并生成Grade.txt文件。

4.设计实现过程

 

 

 5.关键代码展示与说明

主函数:

 1 int main()
 2 {
 3         int subnumber,max;
 4         int opnum;
 5         int caseone(int numb1,int numb2,int max,int i);        //函数的声明
 6         int casetwo(int numb1,int numb2,int numb3,int max,int i);
 7         FILE *fp;
 8         fp=fopen("Exercises.txt","w");
 9         fp=fopen("Answers.txt","w");            //建立文件
10         fclose(fp);
11         srand((unsigned)time(0));                //为保证每次运行程序的时候生成的题目都不一样
12         printf("***************随机生成四则运算题目*****************************\n");
13         printf("1.使用 -n 参数控制生成题目的个数\n");
14         printf("2.使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围\n");
15         printf("请输入n:");
16         scanf("%d",&subnumber); 
17         printf("请输入r:");
18         scanf("%d",&max); 
19         printf("题目生成中,请稍后...\n"); 
20         for(i=1;i<=subnumber;i++)        //for循环生成题目
21         {
22             opnum=rand()%2+1;
23             switch(opnum)        
24             {
25             case 1 :                            //当随机数是生成1个运算符时
26                 numb1=rand()%max;
27                 numb2=rand()%max;
28                 caseone(numb1,numb2,max,i);        //执行函数的同时得同时执行把表达式输出到txt文件中
29                 break;
30             case 2 :            //当随机数是生成2个运算符时
31                 numb1=rand()%max;
32                 numb2=rand()%max;
33                 numb3=rand()%max;
34                 casetwo(numb1,numb2,numb3,max,i);
35                 break;    
36             default:printf("ERROR\n");
37             }
38         }
39         char file1[10],file2[10],s[3];
40         int *exe;
41         int *an;
42         printf("校对文件答案并生成Grade.txt文件\n");
43         printf("请输入练习文本:\n"); 
44         scanf("%s",file1);
45         printf("请输入答案文本:\n"); 
46         scanf("%s",file2);
47         int n;
48         n=Count(file1);
49         exe=(int*)malloc(n*sizeof(int));
50         an=(int*)malloc(n*sizeof(int));
51         GetAnswers(file1,exe);
52         GetAnswers(file2,an);
53         Put_Judge(exe,an,n);
54         return 0;
55 
56 }
View Code

子函数:

  1 int numb1,numb2,numb3,i;    //随机生成的数值
  2 int op1,op2;    //运算符
  3 FILE *fp;    //指向答案文件 
  4 FILE *fb;    //指向题目文件 
  5 
  6 int gcd(int numb1,int numb2)    //辗转相除法 
  7 {
  8         if(numb2==0) return numb1;
  9         return    gcd(numb2,numb1%numb2);    
 10 }
 11 
 12 int caseone(int numb1,int numb2,int max,int i)        
 13 {    
 14         int gcd(int numb1,int numb2);
 15         int temp;    //中间值
 16         int result;
 17         op1=rand()%4+1;        //op1的生成值为1到4,分别对应加法,减法,乘法,除法
 18         fb=fopen("Exercises.txt","a");
 19         fp=fopen("Answers.txt","a");    //打开文件
 20         switch(op1){
 21             case 1:        //op1为1的时候,执行加法操作
 22                 result=numb1+numb2;
 23                 fprintf(fp,"%d.答案=%d\n",i,result);        //写入文件
 24                 fclose(fp);            //关闭文件
 25                 fprintf(fb,"%d.%d+%d=%d\n",i,numb1,numb2,result);        
 26                 fclose(fb);    
 27                 break;
 28             case 2 :        //op1为2的时候,执行减法操作;
 29                 if(numb1<numb2)        //为保证结果不为零,当出现生成的被减数比减数小,进行两者值的交换
 30                 {
 31                 temp=numb1;
 32                 numb2=numb1;
 33                 numb1=temp;
 34                 }
 35                 result=numb1-numb2;
 36                 fprintf(fp,"%d.答案=%d\n",i,result);
 37                 fclose(fp);
 38                 fprintf(fb,"%d.%d-%d =%d\n",i,numb1,numb2,result);
 39                 fclose(fb);
 40                 break;
 41             case 3 :        //op1为3的时候,执行乘法操作;
 42                 result=numb1*numb2;
 43                 fprintf(fp,"%d.答案=%d\n",i,result);
 44                 fclose(fp);
 45                 fprintf(fb,"%d.%d*%d =%d\n",i,numb1,numb2,result);
 46                 fclose(fb);
 47                 break;
 48             case 4 :        //op1为4的时候,执行除法操作;
 49                 if(numb2==0)
 50                     numb2=rand()%max+1;
 51                 temp=gcd(numb1,numb2);
 52                 fprintf(fp,"%d.答案=%d/%d\n",i,numb1/temp,numb2/temp);
 53                 fclose(fp);
 54                 fprintf(fb,"%d.%d/%d =%d/%d\n",i,numb1,numb2,numb1/temp,numb2/temp);
 55                 fclose(fb);
 56                 break;
 57             default:printf("ERROR\n");
 58         }
 59         return 0;
 60 }
 61 
 62 int casetwo(int numb1,int numb2,int numb3,int max,int i)
 63 {    
 64         int gcd(int numb1,int numb2);
 65         int result, result1,temp,temp1;
 66         op1=rand()%4+1;        
 67         op2=rand()%4+1;                        //op1,op2的生成值为1到4,分别对应加法,减法,乘法,除法
 68         fp=fopen("Answers.txt","a");
 69         fb=fopen("Exercises.txt","a");
 70         switch(op1){
 71             case 1 :        
 72                 switch(op2){
 73                 case 1 :                    //op1,op2均为加法的情况
 74                     result1=numb1+numb2;
 75                     result=result1+numb3;
 76                     fprintf(fp,"%d.答案=%d\n",i,result);
 77                     fclose(fp);
 78                     fprintf(fb,"%d.%d+%d+%d%=%d\n",i,numb1,numb2,numb3,result);
 79                     fclose(fb);
 80                     break;
 81                 case 2 :                    //op1为加法,op2为减法的情况
 82                     result1=numb1+numb2;
 83                     if(result1<numb3)
 84                     {
 85                         temp=numb3;
 86                         numb3=numb2;
 87                         numb2=temp;
 88                     }
 89                     result=numb1+numb2-numb3;
 90                     fprintf(fp,"%d.答案=%d\n",i,result);
 91                     fclose(fp);
 92                     fprintf(fb,"%d.%d+%d-%d%=%d\n",i,numb1,numb2,numb3,result);
 93                     fclose(fb);
 94                     break;
 95                 case 3 :                    //op1为加法,op2为乘法的情况
 96                     result1=numb2*numb3;
 97                     result=numb1+result1;
 98                     fprintf(fp,"%d.答案=%d\n",i,result);
 99                     fclose(fp);
100                     fprintf(fb,"%d.%d+%d*%d%=%d\n",i,numb1,numb2,numb3,result);
101                     fclose(fb);
102                     break;
103                 case 4 :                    //op1为加法,op2为除法的情况
104                     if(numb3==0)
105                     {
106                     numb3=rand()%max+1;
107                     }
108                     temp=gcd(numb2,numb3);
109                     fprintf(fp,"%d.答案=%d'%d/%d\n",i,numb1,numb2/temp,numb3/temp);
110                     fclose(fp);
111                     fprintf(fb,"%d.%d+%d/%d%=%d'%d/%d\n",i,numb1,numb2,numb3,numb1,numb2/temp,numb3/temp);
112                     fclose(fb);
113                     break;
114                 default:printf("ERROR\n");
115                 }
116                 break;
117             case 2 :        
118                 switch(op2){
119                     case 1 :                    //op1为减法,op2为加法的情况
120                         if(numb1<numb2)            //为保证结果不为零,当出现生成的被减数比减数小,进行两者值的交换
121                         {
122                             temp=numb1;
123                             numb2=numb1;
124                             numb1=temp;
125                         }
126                         result=numb1-numb2+numb3;
127                         fprintf(fp,"%d.答案=%d\n",i,result);
128                         fclose(fp);
129                         fprintf(fb,"%d.%d-%d+%d%=%d\n",i,numb1,numb2,numb3,result);
130                         fclose(fb);
131                         break;
132                     case 2 :                    //op1为减法,op2为减法的情况
133                         do{
134                         numb1=rand()%max;
135                         numb2=rand()%max;
136                         numb3=rand()%max;
137                         }while((numb1-numb2-numb3)<=0);
138                         result=numb1-numb2-numb3;
139                         fprintf(fp,"%d.答案=%d\n",i,result);
140                         fclose(fp);
141                         fprintf(fb,"%d.%d-%d-%d%=%d\n",i,numb1,numb2,numb3,result);
142                         fclose(fb);
143                         break;
144                     case 3 :                    //op1为减法,op2为乘法的情况
145                         do{
146                         numb1=rand()%max;
147                         numb2=rand()%max;
148                         numb3=rand()%max;
149                         }while((numb1-numb2*numb3)<0);
150                         result=numb1-numb2*numb3;
151                         fprintf(fp,"%d.答案=%d\n",i,result);
152                         fclose(fp);
153                         fprintf(fb,"%d.%d-%d*%d%=%d\n",i,numb1,numb2,numb3,result);
154                         fclose(fb);
155                         break;
156                     case 4 :                    //op1为减法,op2为除法的情况
157                         do{
158                         numb1=rand()%max;
159                         numb2=rand()%max;
160                         numb3=rand()%max;
161                         }while((numb1-numb2/numb3)<0||numb3==0);
162                         result=numb1-numb2/numb3;
163                         fprintf(fp,"%d.答案=%d\n",i,result);
164                         fclose(fp);
165                         fprintf(fb,"%d.%d-%d/%d%=%d\n",i,numb1,numb2,numb3,result);
166                         fclose(fb);
167                         break;
168                 default:printf("ERROR\n");
169                 }
170                 break;
171             case 3 :        
172                 switch(op2){
173                     case 1 :                    //op1为乘法,op2为加法的情况
174                         result=numb1*numb2+numb3;
175                         fprintf(fp,"%d.答案=%d\n",i,result);
176                         fclose(fp);
177                         fprintf(fb,"%d.%d*%d+%d%=%d\n",i,numb1,numb2,numb3,result);
178                         fclose(fb);
179                         break;
180                     case 2 :                    //op1为乘法,op2为减法的情况
181                         do{
182                         numb1=rand()%max;
183                         numb2=rand()%max;
184                         numb3=rand()%max;
185                         }while((numb1*numb2-numb3)<0);
186                         result=numb1*numb2-numb3;
187                         fprintf(fp,"%d.答案=%d\n",i,result);
188                         fclose(fp);
189                         fprintf(fb,"%d.%d*%d-%d%=%d\n",i,numb1,numb2,numb3,result);
190                         fclose(fb);
191                         break;
192                     case 3 :                    //op1为乘法,op2为乘法的情况
193                         result=numb1*numb2*numb3;
194                         fprintf(fp,"%d.答案=%d\n",i,result);
195                         fclose(fp);
196                         fprintf(fb,"%d.%d*%d*%d%=%d\n",i,numb1,numb2,numb3,result);
197                         fclose(fb);
198                         break;
199                     case 4 :                    //op1为乘法,op2为除法的情况
200                         do{
201                         numb3=rand()%max;
202                         }while(numb3==0);
203                         temp1=numb1*numb2;
204                         temp=gcd(temp1,numb3);
205                         fprintf(fp,"%d.答案=%d/%d\n",i,temp1/temp,numb3/temp);
206                         fclose(fp);
207                         fprintf(fb,"%d.%d*%d/%d%=%d/%d\n",i,numb1,numb2,numb3,temp1/temp,numb3/temp);
208                         fclose(fb);
209                         break;
210                 default:printf("ERROR\n");
211                 }
212                 break;
213             case 4 :        
214                 switch(op2){
215                     case 1 :                    //op1为除法,op2为加法的情况
216                         do{
217                         numb2=rand()%max;
218                         }while(numb2==0);
219                         temp=gcd(numb1,numb2);
220                         fprintf(fp,"%d.答案=%d'%d/%d\n",i,numb3,numb1/temp,numb2/temp);
221                         fclose(fp);
222                         fprintf(fb,"%d.%d/%d+%d%=%d'%d/%d\n",i,numb1,numb2,numb3,numb3,numb1/temp,numb2/temp);
223                         fclose(fb);
224                         break;
225                     case 2 :                    //op1为除法,op2为减法的情况
226                         do{
227                         numb3=rand()%max;
228                         }while(    (numb1/numb2-numb3)<0||numb2==0);
229                         result=numb1/numb2-numb3;
230                         fprintf(fp,"%d.答案=%d\n",i,result);
231                         fclose(fp);
232                         fprintf(fb,"%d.%d/%d-%d%=%d\n",i,numb1,numb2,numb3,result);
233                         fclose(fb);
234                         break;
235                     case 3 :                    //op1为除法,op2为乘法的情况
236                         do{
237                         numb2=rand()%max;
238                         }while(numb2==0);
239                         temp1=numb1*numb3;
240                         temp=gcd(temp1,numb2);
241                         result=numb1/numb2*numb3;
242                         fprintf(fp,"%d.答案=%d/%d\n",i,temp1/temp,numb2/temp);
243                         fclose(fp);
244                         fprintf(fb,"%d.%d/%d*%d%=%d/%d\n",i,numb1,numb2,numb3,temp1/temp,numb2/temp);
245                         fclose(fb);
246                         break;
247                     case 4 :                    //op1为除法,op2为除法的情况
248                         do{
249                         numb2=rand()%max;
250                         numb3=rand()%max;
251                         }while(numb3==0||numb2==0);
252                         result=numb1/numb2/numb3;
253                         temp1=numb2*numb3;
254                         temp=gcd(temp1,numb1);
255                         fprintf(fp,"%d.答案=%d/%d\n",i,numb1/temp,temp1/temp);
256                         fclose(fp);
257                         fprintf(fb,"%d.%d/%d/%d%=%d/%d\n",i,numb1,numb2,numb3,numb1/temp,temp1/temp);
258                         fclose(fb);
259                         break;
260                 default:printf("ERROR\n");
261                 }
262                 break;
263             default:printf("ERROR\n");
264         }
265         return 0;
266 }
267 
268 
269 int Count(char file[])  /*计算的题目数量*/ 
270 {
271     FILE *fp1;
272     char a;
273     int x=0;
274     if((fp1=fopen(file,"r"))==NULL)
275     {
276         printf("read file failed!\n");
277         exit(0);
278     }
279     while(!feof(fp1))
280     {
281         a=fgetc(fp1);
282         if(a=='\n')
283             x++;
284     }      
285     fclose(fp1);
286     return x;
287 }
288 
289 void Put_Judge(int *str1,int *str2,int n) /* 答案判断对错函数*/ 
290 {
291     int i,j;
292     FILE *out;
293     out=fopen("Grade.txt","w");
294     for(i=0,j=0;i<n;i++)     /*对的个数*/ 
295         if(*(str1+i)==*(str2+i))
296         j++;    
297     fprintf(out,"Correct:%d (",j);
298     for(i=0;i<n;i++)     /*对的序号*/ 
299     {
300         if(*(str1+i)==*(str2+i))
301         fprintf(out," %d ",(i+1));
302     }
303     fprintf(out,")\n");
304      fprintf(out,"Wrong:%d (",(n-j));
305     for(i=0;i<n;i++)  /*错的序号*/ 
306     {
307         if(*(str1+i)!=*(str2+i))
308         fprintf(out," %d ",(i+1));
309     }
310     fprintf(out,")\n");
311     fclose(out);
312     printf("OK");
313 }
314 
315 void GetAnswers(char infile[],int *str)  /*读取学生答案的函数*/ 
316 {
317     FILE *in;
318     char a;
319     int i=0;
320 if((in=fopen(infile,"r"))==NULL)
321     {
322        printf("read file failed!\n"); 
323        exit(0);
324     } 
325     while(!feof(in))
326     {
327         *(str+i)=0;
328         a=fgetc(in);        
329         if(a!='=') 
330         continue;
331         while(1)
332         {
333             a=fgetc(in);
334             if(a=='\n')break;
335             *(str+i)*=10;    
336             *(str+i)+=(a-48);
337         }
338             printf("%d ",*(str+i));
339             i++;
340     }
341     printf("\n");
342     fclose(in);
343 }
View Code

6.测试运行

生成10道题目:

 

 

 

 生成100道题目:

 

 

 

 生成10000道题目:

 

 

 

 故意将前三道题目答案改错:

 

 

 

7.项目完结个人PSP

 

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning 计划 10 10
· Estimate · 估计这个任务需要多少时间 20 10
Development 开发 1200 1200
· Analysis · 需求分析 (包括学习新技术) 180 120
· Design Spec · 生成设计文档 60 30
· Design Review · 设计复审 (和同事审核设计文档) 20 30
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 60 60
· Design · 具体设计 120 150
· Coding · 具体编码 360 390
· Code Review · 代码复审 60 90
· Test · 测试(自我测试,修改代码,提交修改) 180 210
Reporting 报告 60 90
· Test Report · 测试报告 60 30
· Size Measurement · 计算工作量 10 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 30
合计   2430 2460

 

8.项目小结:

收获:更熟悉psp的操作方法,对c语言文件读写方法有了更深的理解,是一次很好的打码练习,同时作为合作项目,认识到1+1>2的道理,队友的作用不在于分担了多少代码量,关键时刻的提醒和建议往往起到事半功倍的作用。

不足:部分功能未能实现,个人能力需要加强。

 

posted @ 2020-04-02 00:50  Q·B  阅读(674)  评论(0编辑  收藏  举报