2020面向对象程序设计寒假作业2
这个作业属于哪个课程 | 2020面向对象程序设计 |
---|---|
这个作业要求在哪里 | 2020面向对象程序设计寒假作业2 |
这个作业的目标 | 1.实践题 新建一个github仓库,使用git或者github desktop把接下去的编程题的代码及测试脚本传到这个仓库。 请使用.gitignore文件忽略不要上传的文件。 2.编程题(请使用C语言或者C++完成以下题目): (1)继续完成作业一的编程题。 (2)优化架构,一般要求每个函数长度不超过15行。 (3)优化规范,尤其是命名规范。 (4)制作一个编译脚本,运行该脚本可以编译你的代码,可选的脚本语言,python(2.7),windows批处理,powershell,shell。 (5)进行单元测试,即测试每一个函数,并制作一个测试脚本,运行该脚本可以进行测试,并显示测试结果。 (6)添加以下功能: 通过命令行读取一个文件,然后运行这个文件。如我的程序叫lang,lang 1.txt代表我要运行1.txt这个文本中的代码。 |
作业正文 | 实践题 编程题 |
其他参考文献 | 批处理(百度百科) gcc使用指南 头文件相关知识 freopen函数的运用 argc和argv的用法.gitignore文件相关知识gitignore文件创建方法 |
1.实践题
(1)新建一个github仓库,使用git或者github desktop把接下去的编程题的代码及测试脚本传到这个仓库。
如下图,在GitHub desktop中点击file点击new repository,在本地创建新仓库
注意这里有个自带的Git ignore选项可以选择不被上传的文件类型
之后将需要上传的文件添加到刚刚建立的仓库的目录中就可以在GitHub desktop中进行操作。点击Publish repository将仓库同步到GitHub中,点击commit to master将文件同步到远程中,之后便可以在网页中查看到。
(2)请使用.gitignore文件忽略不要上传的文件。
.gitignore文件要在push之前添加在本地仓库的目录中,然后与需同步到远端的文件一起同步,.gitignore文件创建编辑过程如下:
为创建.gitignore文件首先应该下载Git,然后在本地仓库所在的目录中右击选择Git Bash直接进入仓库所在的路径(减少输入仓库绝对路径的麻烦),输入touch .gitignore创建.gitignore文件,如下图:
然后就可以在仓库中找到空白的.gitignore文件
接着就可以对.gitignore文件文件进行编辑,设置过滤规则,这里可以参考该文档,
过滤规则总结如下:
a.跳过单个文件
eg.
project.hello.cpp
b.跳过某格式文件
eg.
.zip
.exe
c.跳过某个文件夹
eg.
Debug/(忽略掉Debug文件夹)
d.过滤掉某个文件夹中的某个文件或某个格式
eg.
hello/*.jpg
hello/hello.exe
e.指定添加某个文件夹、文件或某个格式(前面添加“!”)
eg.
!/hello/
!/hello/test.exe
!*.zip
f.[]过滤掉多个文件或类型
eg.
*.[oa] (忽略掉所有以.a或.o结尾的文件)
最后将.gitignore文件一同上传即可。
2.编程题
(1)继续完成作业一的编程题。
(2)优化架构,一般要求每个函数长度不超过15行。
(3)优化规范,尤其是命名规范。
(4)制作一个编译脚本,运行该脚本可以编译你的代码,可选的脚本语言,python(2.7),windows批处理,powershell,shell。
示例脚本:
gcc 1.cpp -o lang.exe
(5)进行单元测试,即测试每一个函数,并制作一个测试脚本,运行该脚本可以进行测试,并显示测试结果。
示例脚本:
gcc 1.cpp test.cpp -o test.exe
test
(6)在作业一编程题的基础上添加以下功能:
通过命令行读取一个文件,然后运行这个文件。如我的程序叫lang,lang 1.txt代表我要运行1.txt这个文本中的代码。
假设我的1.txt内容如下:
整数 钱包 等于 零
钱包 增加 四
钱包 减少 三
看看 钱包
输入lang 1.txt后,命令行输出一。
编程题要求:
(1)读题,提取出题目的要求。
(2)分解需求,把需求分解为几个你觉得不太相关的模块。
(3)思考每个模块怎么写,可以从简单的模块开始写。
(4)对于不会的问题进行查阅资料。
(4)对于每一个模块设计测试用例。
(5)详细记录下以上每一步,并写在博客中。
Part1 代码优化
(1)原代码分解(作业一)
swap函数
实现单个汉字与数字的转化
char list[11][5]={"零","一","二","三","四","五","六","七","八","九","十"};//运用数组下标和汉字对应关系进行查表更方便
int swap(char a[]){
int i;
for(i=0;i<11;i++){
if(strcmp(list[i],a)==0) return i;
}
}
num函数
在swap函数的基础上将读入的汉字转化为对应数字
int num(char a[]){
char b[10],c[10];
if(strlen(a)==2) return swap(a);//对应零一二等单个汉字表达的数字
else if(strlen(a)==4){b[0]=a[2];b[1]=a[3];b[2]='\0';//对应十一、十二等以十开头的两个汉字表达的数字
if(swap(b)!=10) return 10+swap(b);
else{b[0]=a[0];b[1]=a[1];b[2]='\0';//对应二十、三十等以十结尾的数字
return 10*swap(b);
}
}
else if(strlen(a)==6){b[0]=a[4];b[1]=a[5];b[2]='\0';//对应“几十几”类型的数字
c[0]=a[0];c[1]=a[1];c[2]='\0';
return swap(b)+swap(c)*10;
}
}
judge函数
识别输入的运算条件并计算
int judge(int n,char a[],char b[]){
int sum1;
sum1=num(b);
if(strcmp(a,"增加")==0) return n+sum1;
else if(strcmp(a,"减少")==0) return n-sum1;
else if(strcmp(a,"乘以")==0) return n*sum1;
else if(strcmp(a,"除以")==0) return n/sum1;
}
main函数
int main(){
int i,sum;
char a[100],b[100],c[100],d[100];
scanf("%s",a);
if(!strcmp(a,"整数"))scanf("%s %s %s",a,b,c);
else{printf("输入格式错误\n");
return 0;
}
sum=num(c);
while(scanf("%s %s",d,b)!=EOF){
if(strcmp(d,"看看")){scanf("%s",c);
if(!strcmp(a,d)){sum=judge(sum,b,c);
if(sum>=99||sum<=0){printf("超出可容纳范围\n");return 0;}
}
else {printf("变量不匹配\n");return 0}
}
else{if(sum>=0&&sum<=10){printf("%s\n",list[sum]);
else{if(sum%10==0) printf("%s十\n",list[sum/10]);
else if(sum<=19) printf("十%s\n",list[sum%10]);
else printf("%s十%s\n",list[sum/10],list[sum%10]);
}
}
}
}
return 0;
}
(2)需求分析及优化
a.由(1)中的代码分解可以发现,num函数以及main函数过于冗长,特别是main函数,因此需要对其进行简化。对main函数进行分析可以发现,其中的数字转化输出部分可以用自定义函数来代替,代码如下:
void change_print(int sum){
if(sum>=0&&sum<=10){printf("%s\n",list[sum]);}
else {if(sum%10==0) printf("%s十\n",list[sum/10]);
else if(sum<=19) printf("十%s\n",list[sum%10]);
else printf("%s十%s\n",list[sum/10],list[sum%10]);
}
}
为保持“看看”的重复查询功能,main函数不再继续简化
b.因为judge函数中“乘以”“除以”的识别运算功能用处较小且与题目无关,故删去,代码如下:
int judge(int n,char a[],char b[]){
int sum1;
sum1=num(b);
if(strcmp(a,"增加")==0) return n+sum1;
else if(strcmp(a,"减少")==0) return n-sum1;
}
Part2 功能拓展
(1)制作一个编译脚本,运行该脚本可以编译你的代码,可选的脚本语言,python(2.7),windows批处理,powershell,shell。
我选用的是Windows批处理,通过查找有关批处理的资料可以得到,可以在系统自带记事本中输入批处理指令再将拓展名改成.bat来生成批处理文件,即我们所需要的脚本。但是系统自带记事本功能较弱,操作起来比较麻烦,我就选用了能力更强大的Notepad++来进行编辑。
制作的脚本如下:
@echo off
title 编译脚本
gcc character.cpp -o character.exe
if exist character.exe echo 编译成功!
pause
无法对源文件进行编译以及脚本命名乱码。
然后捣鼓了一会发现生成的.bat文件所指定的路径就是文件保存位置的路径,所以可以知道只要将批处理文件保存在与源文件相同的位置就能解决,至于脚本命名乱码的问题,Notepad++中可以改变编码的类型只要改成ANSI编码就可以解决问题,操作如下:
由此可以得知编译脚本制作成功
(2)进行单元测试,即测试每一个函数,并制作一个测试脚本,运行该脚本可以进行测试,并显示测试结果。
这里纠结了很久如何进行单元测试,原本的想法是直接对需要测试的函数编写一段测试代码其中包含需测试的函数,但通过助教的提示以及参考前面提交同学的测试模式,可以将需要测试的函数写入头文件中,然后调用函数来测试,其实具体思想差不多。
main.h文件如下:
#include
#include
char list[11][5]={"零","一","二","三","四","五","六","七","八","九","十"};
int swap(char a[]){
int i;
for(i=0;i<11;i++){
if(strcmp(list[i],a)==0) return i;
}
}
int num(char a[]){char b[10],c[10];
if(strlen(a)==2) return swap(a);
else if(strlen(a)==4){b[0]=a[2];b[1]=a[3];b[2]='\0';
if(swap(b)!=10) return 10+swap(b);
else{b[0]=a[0];b[1]=a[1];b[2]='\0';
return 10*swap(b);
}
}
else if(strlen(a)==6){b[0]=a[4];b[1]=a[5];b[2]='\0';
c[0]=a[0];c[1]=a[1];c[2]='\0';
return swap(b)+swap(c)*10;
}
}
int judge(int n,char a[],char b[]){
int sum1;
sum1=num(b);
if(strcmp(a,"增加")==0) return n+sum1;
else if(strcmp(a,"减少")==0) return n-sum1;
}
void change_print(int sum){
if(sum>=0&&sum<=10){printf("%s\n",list[sum]);}
else {
if(sum%10==0) printf("%s十\n",list[sum/10]);
else if(sum<=19) printf("十%s\n",list[sum%10]);
else printf("%s十%s\n",list[sum/10],list[sum%10]);
}
}
由于change_print是对变量大小的一个转化输出,可以通过输出的结果对函数进行测试,其余函数则要通过比对模拟结果和输出结果进行判断,对测试函数需进行随机取样保证测试准确。测试代码如下:
//test.cpp
#include"main.h"
void test(int a,int b){
if(a==b) printf("Pass\n");
else printf("Error\n");
}
int main(){
printf("swap函数测试开始\n");
char swap_test[11][5]={"零","一","二","三","四","五","六","七","八","九","十"};
test(swap(swap_test[0]),0);
test(swap(swap_test[1]),1);
test(swap(swap_test[2]),2);
test(swap(swap_test[4]),4);
test(swap(swap_test[5]),5);
test(swap(swap_test[7]),7);
test(swap(swap_test[10]),10);
printf("swap函数测试结束\n");
printf("num函数测试开始\n");
char num_test[6][10]={"五","十","十八","三十六","六十","九十八"};
test(num(num_test[0]),5);
test(num(num_test[1]),10);
test(num(num_test[2]),18);
test(num(num_test[3]),36);
test(num(num_test[4]),60);
test(num(num_test[5]),98);
printf("num函数测试结束\n");
printf("judge函数测试开始\n");
int n=50;char a[]={"增加"},b[]={"减少"}, c[6][10]={"十一","二十","六","四十八","三","零"};
test(judge(n,a,c[0]),61);
test(judge(n,b,c[1]),30);
test(judge(n,a,c[2]),56);
test(judge(n,b,c[3]),2);
test(judge(n,a,c[4]),53);
test(judge(n,b,c[5]),50);
printf("judge函数测试结束\n");
printf("chang_print函数测试开始\n");
printf("sum=6 ");change_print(6);
printf("sum=20 ");change_print(20);
printf("sum=11 ");change_print(11);
printf("sum=79 ");change_print(79);
printf("sum=34 ");change_print(34);
printf("sum=0 ");change_print(0);
printf("chang_print函数测试结束\n");
printf("测试完成\n");
return 0;
}
参照以上制作编译脚本的方法制作测试脚本,为保证编译运行正确,可将头文件、测试代码、脚本保存在同一路径下。脚本代码如下:
@echo off
title 测试脚本
gcc test.cpp -o test.exe
text.exe
pause
由此可知,单元测试成功,测试脚本制作成功
(3)在作业一编程题的基础上添加以下功能:
通过命令行读取一个文件,然后运行这个文件。如我的程序叫lang,lang 1.txt代表我要运行1.txt这个文本中的代码。
这一块内容也是卡了很久一直没有思路,直接搜索也没有找到直接的资料,通过借鉴已提交同学的做法,了解到这里需要用到freopen()函数以及main函数的参数argc和argv,因此我查找了相关资料(已在导览中贴出),对原先的代码进行了修改,代码如下:
#include
#include
char list[11][5]={"零","一","二","三","四","五","六","七","八","九","十"};
int swap(char a[]){
int i;
for(i=0;i<11;i++){
if(strcmp(list[i],a)==0) return i;
}
}
int num(char a[]){char b[10],c[10];
if(strlen(a)==2) return swap(a);
else if(strlen(a)==4){b[0]=a[2];b[1]=a[3];b[2]='\0';
if(swap(b)!=10) return 10+swap(b);
else{b[0]=a[0];b[1]=a[1];b[2]='\0';
return 10*swap(b);
}
}
else if(strlen(a)==6){b[0]=a[4];b[1]=a[5];b[2]='\0';
c[0]=a[0];c[1]=a[1];c[2]='\0';
return swap(b)+swap(c)*10;
}
}
int judge(int n,char a[],char b[]){
int sum1;
sum1=num(b);
if(strcmp(a,"增加")==0) return n+sum1;
else if(strcmp(a,"减少")==0) return n-sum1;
}
void change_print(int sum){
if(sum>=0&&sum<=10){printf("%s\n",list[sum]);}
else {
if(sum%10==0) printf("%s十\n",list[sum/10]);
else if(sum<=19) printf("十%s\n",list[sum%10]);
else printf("%s十%s\n",list[sum/10],list[sum%10]);
}
}
int main(int argc,char *argv[]){
if(argc>1){
freopen(argv[1],"r",stdin);
}
int i,sum;
char a[100],b[100],c[100],d[100];
scanf("%s",a);
if(!strcmp(a,"整数")) scanf("%s %s %s",a,b,c);
else{printf("输入格式错误\n");
return 0;
}
sum=num(c);
while(scanf("%s %s",d,b)!=EOF){
if(strcmp(d,"看看")){scanf("%s",c);
if(!strcmp(a,d)){sum=judge(sum,b,c);
if(sum>=99||sum<=0){printf("超出可容纳范围\n");
return 0;
}
}
else{printf("变量不匹配\n");
return 0;
}
}
else change_print(sum);
}
return 0;
}
制作txt文件如下:
file1.txt
file2.txt
首先通过命令行cd指令找到file1与file2所在的位置,再进行操作,如下图
由此可知,添加新功能成功
Part3 思考与总结
虽然过程坎坷就像摸着石头过河,很多东西要现查现学,但是通过这次作业,还是收获到了不少东西。学到了一些关于Windows批处理的知识,学习了如何制作脚本,学会了如何较为系统的进行单元测试,也学到了一些有关文件输入输出相关的知识。不过虽然说摸索了一些新的东西,但是对于这些东西的理解都不太深入,需要进一步的学习和挖掘,接下来我应该会更进一步系统学习有关批处理以及文件输入输出的知识。