软工实践寒假作业(2/2)
作者:129龚俊鹏
博客:https://www.cnblogs.com/gjinpn/
这个作业属于哪个课程 | 2020春S班 |
---|---|
作业要求 | 软工实践寒假作业(2/2) |
作业目标 | 对GitHub有一个初使用、对自己的代码进行相关的规范要求、设计一个简单的疫情统计程序以及使用单元测试对程序进行评估检查 |
作业正文 | 221701129龚俊鹏 |
参考文献 | 博客、知乎、贴吧 |
GitHub仓库地址
此次作业代码部分GitHub地址:https://github.com/129JP/InfectStatistic-main
PSP
PSP是卡耐基梅隆大学(CMU)的专家们针对软件工程师所提出的一套模型:Personal Software Process (PSP, 个人开发流程,或称个体软件过程)。
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 120分钟 | 100 |
Estimate | 估计这个任务需要多少时间 | 70 | 80 |
Development | 开发 | 300 | 300 |
Analysis | 需求分析 (包括学习新技术) | 60 | 60 |
Design Spec | 生成设计文档 | 30 | 60 |
Design Review | 设计复审 | 20 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
Design | 具体设计 | 80 | 100 |
Coding | 具体编码 | 900 | 800 |
Code Review | 代码复审 | 200 | 150 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 60 |
Reporting | 报告 | 70 | 80 |
Test Repor | 测试报告 | 30 | 50 |
Size Measurement | 计算工作量 | 30 | 30 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 60 | 60 |
合计 | 2060 | 1980 |
解题思路描述
- 第一次看到题目的时候有点生疏,但是多看几遍之后发现这次程序的题目其实就是对于字符串的相关处理,根据输入的字符串然后匹配执行相应的操作,以前有做过类似的,所以思路一下就捋顺了很多。但是以前的编程都是采用C/C++来编程的,有很多的字符串处理函数需要自己编写(或许我不知道),代码的开发效率可能会下降,所以这次决定采用JAVA来开发此次项目。
- 根据输入的字符串,多次匹配,执行多次的操作,最后结果保存到相应文件。
- 创建多个字符串数组,暂时存储相关信息。
- 读取文件时,编写相应函数方法Read,对于读取出来的内容用正则表达式来进行匹配处理,这里采用正则表达式会简单很多。
- 数据统计处理的时候,也是用正则表达式处理,再用numString.replaceAll("").trim()和Integer.parseInt得到整数,存到相应的数组。
- 最后统计得到的数据存入文本。
- 检查特殊情况,比如指令一些特别的输入,需要做一些相应的处理。
设计实现过程
- 第一次看到题目的时候有点生疏,但是多看几遍之后发现这次程序的题目其实就是对于字符串的相关处理,根据输入的字符串然后匹配执行相应的操作,以前有做过类似的,所以思路一下就捋顺了很多。但是以前的编程都是采用C/C++来编程的,有很多的字符串处理函数需要自己编写(或许我不知道),代码的开发效率可能会下降,所以这次决定采用JAVA来开发此次项目。
- 根据输入的字符串,多次匹配,执行多次的操作,最后结果保存到相应文件。
- 创建多个字符串数组,暂时存储相关信息。
- 读取文件时,编写相应函数方法Read,对于读取出来的内容用正则表达式来进行匹配处理,这里采用正则表达式会简单很多。
- 数据统计处理的时候,也是用正则表达式处理,再用numString.replaceAll("").trim()和Integer.parseInt得到整数,存到相应的数组。
- 最后统计得到的数据存入文本。
- 检查特殊情况,比如指令一些特别的输入,需要做一些相应的处理。
以下是流程图
代码说明
根据命令行参数字符串匹配进行相关处理:这部分这么设计的原因是为了和后面的代码相匹配,有些指令是可以不带参数进行默认处理的,所以要有一些标志来进行相关定位,比如循环里面的SIZE和ESP...
for(int i=0;i<args.length;i++)
{
switch(args[i])
{
case "-date":
SIZE=1;
continue;
case "-log":
SIZE=2;
continue;
case "-out":
SIZE=3;
continue;
case "-type":
SIZE=4;
esp=1;
continue;
case "-province":
SIZE=5;
esp1=1;
continue;
}
if(SIZE==1)
{
Date=args[i];
}
else if(SIZE==2)
{
int Flag1=i;
AddressIn=args[Flag1];
}
else if(SIZE==3)
{
int Flag2=i;
AddressOut=args[Flag2];
}
else if(SIZE==4)
{
for(;i<args.length;i++)
{
if(!args[i].equals("-date")&&!args[i].equals("-out")&&!args[i].equals("-log")&&!args[i].equals("-province"))
{
if(args[i].equals("ip"))
list[0]="感染患者";
else if(args[i].equals("sp"))
list[1]="疑似患者";
else if(args[i].equals("cure"))
list[2]="治愈";
else if(args[i].equals("dead"))
list[3]="死亡";
Tsize++;
}
else
{
if(Tsize==0)
{
list[0]="感染患者";
list[1]="疑似患者";
list[2]="治愈";
list[3]="死亡";
Tsize=4;
}
i--;
break;
}
}
}
else if(SIZE==5)
{
for(int j=0;j<32;j++)
{
if(args[i].equals(ADN[j]))
{
ADNSize[Psize]=j;
Psize++;
}
}
Arrays.sort(ADNSize);
}
}
要对输入地址进行判断,如果没有输入读或者写路径,就会抛出错误
if(AddressOut!=null&&AddressIn!=null)
getFilesName是根据Date是否为空返回需要处理的日志文件的相应路径名,他可以获得用户输入路径下的全部文件名,根据需要输出
filesNames=getFilesName(AddressIn,Date);
-----------------------------------------------------
public static ArrayList<String> getFilesName(String filepath,String DATE){
ArrayList<String> files = new ArrayLgetFilesNameist<String>();
String LS=null;
File file = new File(filepath);
File[] tempLists = file.listFiles();
if(DATE!=null)
{
LS=filepath+"\\"+DATE+".log.txt";
}
else
{
LS=tempLists[tempLists.length-1].toString();
}
for (int i = 0; i < tempLists.length; i ++)
{
if (tempLists[i].isFile()&&tempLists[i].toString().compareTo(LS)<=0)
{
files.add(tempLists[i].toString());
}
}
/*for (int i = 0; i < files.size(); i++)
{
System.out.println(files.get(i));
}*/
return files;
}
而下面就要对需要获取的省份或者全国进行表示,将这些地名存在condition1里面,这里面可能要用到getFilesName获得的文件名序列,如果用户从未输入过province这个指令,那么condition1就会保存所有在要求日志文件中出现的地名,而且第一个一定是全国。
if(esp1!=0)
{
condition1=new String[Psize];
for(int j=0;j<Psize;j++)
{
condition1[Psize-j-1]=ADN[ADNSize[31-j]];
}
}
else {
int LS[]=new int[32];
for(int i=1;i<32;i++)
{
for(int j=0;j<filesNames.size();j++)
if(Pnum(ADN[i],filesNames.get(j))==1)
{
LS[Psize]=i;
Psize++;
}
}
Psize++;
Arrays.sort(LS);
condition1=new String[Psize];
for(int j=0;j<Psize;j++)
{
condition1[Psize-j-1]=ADN[LS[31-j]];
}
condition1[0]="全国";
}
根据要求获取日志文件中的数据,保存在condition2里面,这里用到了自己编写的Read函数方法
for(int i=0;i<filesNames.size();i++)
{
for(int j=0;j<Psize;j++)
{
Read(filesNames.get(i),condition1[j],condition2[j]);
}
}
Read函数方法就是为了获取获取日志文件里面有出现的用户需要输出的数据,对应的保存在condition2里面
//read函数是这个程序里面最主要的,也是处理的情况最复杂的一个
public static String Read(String address1,String condition1,int condition2[])//定义
//主要代码1,这是日志可能出现的10种情况,全部写出来之后,用正则表达式去匹配
pattern[0] = condition1+".*新增.*感染患者.*";
pattern[1] = condition1+".*新增.*疑似患者.*";
pattern[2] = condition1+".*感染患者.*流入.*";
pattern[3] = condition1+".*疑似患者.*流入.*";
pattern[4] = condition1+".*死亡.*";
pattern[5] = condition1+".*治愈.*";
pattern[6] = condition1+".*疑似患者.*确诊感染.*";
pattern[7] = condition1+".*排除.*疑似患者.*";
pattern[8] = ".*感染患者.*流入.*"+condition1+".*";
pattern[9] = ".*疑似患者.*流入.*"+condition1+".*";
//主要代码2,构造文件流,用readline一行一行的读取,可以方便对于数据的处理,而且需要注意的是,这里要指明编码方式,否则可能出现乱码
FileInputStream fr=new FileInputStream(file);
InputStreamReader fsr=new InputStreamReader(fr,"UTF-8");
BufferedReader br = new BufferedReader(fsr);//读取文件
s = br.readLine();
//主要代码3,这个是对匹配出现的各种情况的处理,提取整数的处理,采用正则表达式,将字符串只留下数字,再用Integer.parseInt转化为整形,存到condition2里面.
String num="[^0-9]";
Pattern p = Pattern.compile(num);
Matcher numString = p.matcher(s);
int NUM=Integer.parseInt(numString.replaceAll("").trim());//提取整数
将处理得到的所需数据和一些输出表达规范相结合,将其输出到目标文件中,这是调用了FileIn函数方法
FileIn(AddressOut,condition1[j]);
--------------------------------------
public static void FileIn(String address,String content)
{
File f = new File(address);
FileWriter fw;
try
{
fw=new FileWriter(f,true);
if(content!="\n")
fw.write(content+" ");//将字符串写入到指定的路径下的文件中
else
fw.write(content);
fw.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
还有一个函数方法是Pnum,这是用于当用户没有输入province指令时,查找需要处理的日志文件中是否有该地名。
public static int Pnum(String Province,String address1)
{
File file = new File(address1);
StringBuilder result = new StringBuilder();
int m=0;
try
{
FileInputStream fr=new FileInputStream(file);
InputStreamReader fsr=new InputStreamReader(fr,"UTF-8");
BufferedReader br = new BufferedReader(fsr);//构造一个BufferedReader类来读取文件
String s = null;
while((s = br.readLine())!=null)
{
String pattern=".*"+Province+".*";
if(Pattern.matches(pattern,s))
{
m=1;
return m;
}
}
br.close();
fsr.close();
fr.close();
}
catch(Exception e)
{
e.printStackTrace();
}
return m;
}
单元测试截图和描述
本次单元测试的数据全部来自example/result下提供了三个测试用例
-
对-date 2020-01-22 情况进行测试,结果如下,符合预期
-
对-date 2020-01-23 情况进行测试,结果如下,符合预期
-
对-date 2020-01-27 情况进行测试,结果输如下,符合预期
-
对没有date指令只有log和out的情况进行测试,应该处理当前最新的日志及以前,输出结果应该与第三个测试单元一致,结果如下,符合预期
-
当日期超出范围,报错如下
-
带上province参数,因为北京没有出现在日志文件中,所以全部数据为0,输出如下,符合预期
-
带上province参数,福建和北京,因为北京没有出现在日志文件中,所以全部数据为0,又北京排在福建前面,所以将乱序纠正
8.带上-type参数,sp表示疑似患者,按输入顺序输出
-
带上-type参数,sp表示疑似患者,dead表示死亡,按顺序输出
-
带上-type指令,但是参数缺省,全输出
-
province指令没有时,输出全国和日志包含的地名,
单元测试覆盖率优化和性能测试
覆盖率
覆盖率达到了87,在大量的单元测试中,覆盖率都有80%以上,在刚开始的时候其实只有75%左右,通过设置一下变量和判断语句,减少了代码的冗余度,精简了代码
性能测试
作业采用Jprofiler来测试程序性能,
git仓库链接和代码规范链接
git仓库链接:https://github.com/129JP/InfectStatistic-main
代码规范链接:https://github.com/129JP/InfectStatistic-main/blob/master/221701129/codestyle.md
心路历程和收获
刚开始看到这个题目真的是一头雾水,第一次看到题目的时候有点生疏,但是多看几遍之后发现这次程序的题目其实就是对于字符串的相关处理,根据输入的字符串然后匹配执行相应的操作,以前有做过类似的,所以思路一下就捋顺了很多。但是以前的编程都是采用C/C++来编程的,有很多的字符串处理函数需要自己编写(或许我不知道),代码的开发效率可能会下降,所以这次决定采用JAVA来开发此次项目。但是还有一个问题就是采用JAVA的话我其实不是很熟悉的,JAVA的编程量其实没有很多,只完成过一次大作业,所以对于很多的函数方法特别是命令行参数这些都不是很熟悉,所以还是有点担心。
在开发过程中,我本着以下的原则进行开发的,
在性能测试方面,本来想着按照下面的步骤完成的,发现好难啊,所以我就只选取了一部分相关的进行测试,但是总体还是有所改进的。
这次作业的完成我收获颇丰,进一步对于JAVA有了熟悉感,毕竟很久没有写JAVA代码了,其中最主要的是,这次认真的学习了正则表达式的使用以及规范,以前虽然有去了解这些方法,但是没有像这次作业一样认真的研究,发现正则表达式的使用能够很大程度的缩减一些繁琐的代码。而且代码规范的书写,这其实我在以前就写过一份关于自己的一份文档了,所以代码规范问题我其实是有着自己的规范的,虽然不一定合理,但是总体是这样的。
技术路线图相关的5个仓库
技术 | 仓库链接 | 简介 |
---|---|---|
Web技术介绍 | https://github.com/qianguyihao/Web | 这里面有大多数平时我们开发web项目需要用到的技术和知识点,对于初学者来说很有用 |
web相关例子 | https://github.com/hoisie/web | 这里面有一些web例子的相关介绍以及说明,对于新手是有帮助的,可以借助里面规范代码 |
standardnotes/web | https://github.com/standardnotes/web | 主要和markdown、snippets、privacy、notes、secure、encrypted相关 |
web JS | https://github.com/bitwarden/web | javascript html bootstrap angular typescript bitwarden |
springboot | https://github.com/527515025/springBoot | 里面介绍了该框架和其他组件的结合 jpa、mybatis、websocket、security、shiro、cache |