这个作业属于哪个课程 | <2020春S班> |
---|---|
这个作业要求在哪里 | <作业要求> |
这个作业的目标 | <编写疫情统计程序、学习GitHub使用、学习开发有关知识> |
作业正文 | https://www.cnblogs.com/xwh130/p/12331067.html |
其他参考文献 | 菜鸟教程、CSDN |
1.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 40 | 45 |
Estimate | 估计这个任务需要多少时间 | 40 | 45 |
Development | 开发 | 600 | 570 |
Analysis | 需求分析 (包括学习新技术) | 30 | 25 |
Design Spec | 生成设计文档 | 20 | 20 |
Design Review | 设计复审 | 15 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 5 |
Design | 具体设计 | 20 | 24 |
Coding | 具体编码 | 400 | 376 |
Code Review | 代码复审 | 60 | 70 |
Test | 测试(自我测试,修改代码,提交修改) | 45 | 40 |
Reporting | 报告 | 95 | 100 |
Test Repor | 测试报告 | 30 | 40 |
Size Measurement | 计算工作量 | 30 | 30 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 35 | 30 |
合计 | 735 | 715 |
2.解题思路
拿到题目刚开始有点不知所措(可能是理解能力的问题),后来和同学讨论了一下明白了具体要求,
有了一个大致思路。在实践中慢慢摸索出来整个程序的编写。
1.首先我选择的是Java语言,对于所要求的命令可以用args数组读取。
2.对于各个省份的数据存储使用类Province,并使用数组将全国及各省按首字母排序。
3.对数据处理并正确更新对应Province中的各个值(不能忘记初始化为0)。
4.根据命令的要求将所需要数据写入对应的文件中。
3.设计实现过程
模块图:
主要分为4个模块:
1.文件读入:根据用户的命令读取相应的文件。
2.Province类:定义Province类用来存储各个省的数据。
3.数据处理:按要求处理各个类型数据,并保存给对应省。
4.命令输出:按照用户输入的命令,将所需的数据输出到对应的文件。
流程图:
1.首先需要正确的打开用户指定的文件。
2.定义Province类用来存储各个省的数据。
3.按行处理,并以空格提取行内容。
4.分5种情况,细分为8种情况,处理后应更新对象中的变量。
5.按照用户输入的命令,将所需的数据输出到对应的文件。
4.代码说明
1.创建Province类,包含4个成员变量,以省份名构造。
public class Province{
String name; //省份名
int infected = 0; //确诊病例
int mayInfect = 0; //疑似病例
int cure = 0; //治愈出院
int dead = 0; //死亡病例
public Province(String name){
this.name=name;
}
}
2.按首字母顺序构造对象。
String[] pros = new String[]{"全国","安徽","北京","重庆","福建","甘肃","广东",
"广西","贵州","海南","河北","河南","黑龙江","湖北","湖南","吉林","江苏",
"江西","辽宁","内蒙古","宁夏","青海","山东","山西","陕西","上海","四川",
"天津","西藏","新疆","云南","浙江"};
Province[] provinces = new Province[32];
for(int i=0;i<32;i++){
provinces[i] = t.new Province(pros[i]);
prs[i] = " ";
}
3.处理list后的-date,-log,-out,-type,-province,如果没有指定相应的参数,会给予提示。
if(cmd.equals("list")){
for(int i=1;i<args.length;i++){
if(args[i].equals("-date")){
date=args[i+1];
}
else if(args[i].equals("-log")){
local=args[i+1];
}
else if(args[i].equals("-out")){
output=args[i+1];
}
else if(args[i].equals("-type")){
for(int j=0;j<4;j++)
if((j+i+1)<args.length) type[j] = args[i+j+1]; //记录type
if(type[0].isEmpty()){
System.out.println("请指定type!");
System.exit(0);
}
}
else if(args[i].equals("-province")){
for(int j=0;j<args.length-i;j++)
if((j+i+1)<args.length) prs[j] = args[i+j+1]; //记录省
if(prs[0].isEmpty()){
System.out.println("请指定省份!");
System.exit(0);
}
}
}
}
else System.out.println("input right cmd.");
4.读取输入路径下的所有文件,如果date超过了最晚的日志,给出错误提示。
如果目录下并没有文件,也需要给出提示。
String basePath=local;
String[] List=new File(basePath).list();
if (date.compareTo(List[List.length-1])>0) { //判断日期是否合法
System.err.println("日期超出范围");
System.exit(0);
}
date = date+".log.txt";
int res=List[List.length-1].compareTo(date);
if(List[0].equals(null)) System.out.println("选定目录下无文件,请重新选择!"); //判断目录下是否有文件
5.当确定目录下有文件,如果刚好等于最晚一天,那么处理目录下所有文件。否则就从刚好等于date或者第一个小于的文件开始读取。
读取文件注意utf-8,避免乱码。按行读取,并将每行用split函数依据空格提取相关信息。
确定出省份名,以待后面对变量的更新。
之后的思路比较简单,就是对新增、流入、治愈、死亡、疑似排除、疑似确诊这6种情况的处理。
else{
if(res==0) num = List.length;
else {
for(int i=List.length-1;i>0;i--){
if(List[i].compareTo(date)<=0){
num = i+1;
i=0;
}
}
}
for(int j=0;j<num;j++){
try {
/*读取文件*/
FileInputStream f = new FileInputStream(local+List[j]);
InputStreamReader reader = new InputStreamReader(f, "UTF-8");
BufferedReader bf = new BufferedReader(reader);
String str = null,str0 = null;
int a=0,b=0,sum=0;
while ((str = bf.readLine())!= null){
String[] line = str.split(" "); ///以空格间隔提取内容
for(int i=1;i<32;i++){
if(pros[i].equals(line[0])) a = i; //找到省份位置
if(line[2].equals("流入")){
if(pros[i].equals(line[3])) b = i;
}
}
/*1、2新增感染或疑似*/
if(line[1].equals("新增")){
str0=line[3].substring(0,line[3].length()-1);
sum = Integer.parseInt(str0);
if(line[2].equals("感染患者")){
provinces[a].infected += sum;
provinces[0].infected += sum;
}
else if(line[2].equals("疑似患者")){
provinces[a].mayInfect += sum;
provinces[0].mayInfect += sum;
}
}
/*3、4省流入省*/
if(line[2].equals("流入")){
str0=line[4].substring(0,line[4].length()-1);
sum = Integer.parseInt(str0);
if(line[1].equals("感染患者")){
provinces[a].infected -= sum;
provinces[b].infected += sum;
}
else if(line[1].equals("疑似患者")) {
provinces[a].mayInfect -= sum;
provinces[b].mayInfect += sum;
}
}
/*5、6治愈或死亡*/
if(line[1].equals("死亡")||line[1].equals("治愈")){
str0=line[2].substring(0,line[2].length()-1);
sum = Integer.parseInt(str0);
if(line[1].equals("死亡")){
provinces[a].infected -= sum;
provinces[a].dead += sum;
provinces[0].infected -= sum;
provinces[0].dead += sum;
}
else{
provinces[a].infected -= sum;
provinces[a].cure += sum;
provinces[0].infected -= sum;
provinces[0].cure += sum;
}
}
/*7疑似确诊*/
if(line[1].equals("疑似患者")){
if(line[2].equals("确诊感染")){
str0=line[3].substring(0,line[3].length()-1);
sum = Integer.parseInt(str0);
provinces[a].mayInfect -= sum;
provinces[a].infected += sum;
provinces[0].mayInfect -= sum;
provinces[0].infected += sum;
}
}
/*8疑似排除*/
if(line[1].equals("排除")){
str0=line[3].substring(0,line[3].length()-1);
sum = Integer.parseInt(str0);
if(line[2].equals("疑似患者")){
provinces[a].mayInfect -= sum;
provinces[0].mayInfect -= sum;
}
}
}
bf.close();
f.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
6.打开要写的文件,注意:需要判断指定路径的目录是否存在,不存在要创建,不然会报错。
File f = new File(output);
while(!f.getParentFile().exists()) {
boolean mkdir = f.getParentFile().mkdirs();
if (!mkdir) {
throw new RuntimeException("创建目标文件所在目录失败!");
}
}
FileOutputStream fop = new FileOutputStream(f);
OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
7.将-type指定的数据提取出来,放入print数组下。
for(int i=0;i<type.length;i++){
for (int j = 0; j < 32; j++) {
if(type[i].equals("ip"))
print[j] += " 感染患者"+provinces[j].infected+"人 ";
if(type[i].equals("sp"))
print[j] += " 疑似患者"+provinces[j].mayInfect+"人 ";
if(type[i].equals("cure"))
print[j] += " 治愈"+provinces[j].cure+"人 ";
if(type[i].equals("dead"))
print[j] += " 死亡"+provinces[j].dead+"人 ";
}
}
8.如果只含-type那么输出至文件。
如果-type、-province都有,输出指定省份至文件。
当然默认情况就是输出各项不全为0的省份数据至文件。
/*-type*/
if(prs[0].equals(" ")&&!type[0].equals(" "))
for (int i = 0; i < 32; i++) {
if(provinces[i].cure!=0||provinces[i].dead!=0
||provinces[i].infected!=0||provinces[i].mayInfect!=0)
writer.append(print[i]+"
");
}
/*-province*/
else if(type[0].equals(" ")&&!pros[0].equals(" ")){
for(int i=0;i<32;i++){
for(int j=0;j<prs.length;j++)
if (prs[j].equals(provinces[i].name)) {
writer.append(""+provinces[i].name+" 感染患者"+provinces[i].infected+"人 "
+ "疑似患者"+provinces[i].mayInfect+"人 治愈"+provinces[i].cure+"人 死亡"+provinces[i].dead+"人
");
}
}
}
else if(type[0].equals(" ")&&!pros[0].equals(" ")){
for(int i=0;i<32;i++){
for(int j=0;j<prs.length;j++)
if (prs[j].equals(provinces[i].name)) {
writer.append(""+provinces[i].name+" 感染患者"+provinces[i].infected+"人 "
+ "疑似患者"+provinces[i].mayInfect+"人 治愈"+provinces[i].cure+"人 死亡"+provinces[i].dead+"人
");
}
}
}
/*-type -province*/
else if(!type[0].equals(" ")){
for (int i = 0; i < 32; i++) {
String[] s0 = print[i].split(" ");
for(int j=0;j<prs.length;j++)
if(prs[j].equals(s0[0])) writer.append(print[i]+"
");
}
}
/*默认情况*/
else if(type[0].equals(" ")&&prs[0].equals(" "))
for (int i = 0; i < 32; i++) {
if(provinces[i].cure!=0||provinces[i].dead!=0
||provinces[i].infected!=0||provinces[i].mayInfect!=0){
writer.append(""+provinces[i].name+" 感染患者"+provinces[i].infected+"人 "
+ "疑似患者"+provinces[i].mayInfect+"人 治愈"+provinces[i].cure+"人 死亡"+provinces[i].dead+"人
");
}
}
5.单元测试和描述
1.首先对默认仅仅指定输入输出路径下的测试。
2.再对增加-date 2020-02-12情况测试。
3.测试-date、-type(单个参数)。
4.测试-date、-type(多个参数)。
5.测试-date、-province,在这里发现输出空,查找发现自己忘记写仅含-province,遂补充并测试。
修改后的测试结果:
6.测试-date、-type、-province
7.测试日期超出范围,给出提示。
错误提示:
8.测试-province参数乱序时输出结果,按序排列。
9.测试-date 2020-02-13
10.测试-province输出信息为0的省份
6.单元测试覆盖率优化和性能测试
覆盖率
初次进行测试,覆盖率只有60左右,查看代码发现是有些可以跳过的选择分支没有正确处理,导致每次执行都会过一遍那个部分的代码。
还有一些处理用户输入错误的语句,有必要存在,没有改动。
改进后:
覆盖率达到77~93
性能测试
7.git仓库及代码规范链接
8.心路历程和收获
- 心路历程:第一次看到这个题目的时候有点不知所措,面对要解决的问题也没有头绪。当时想着先写写看吧,结果写了没多久,发现要求有些地方我理解出现了偏差,以至于前面写的内容都是无用功。之后想起来高中时候,老师说题目不会做的时候就要多读几遍,有时候就会有新的体会。后来我又把题目看了好几遍,对不清楚的地方询问了同学。
思路有了,开始编程又遇到了一些困难,上网查资料学习,请教同学最终问题还是解决了。编程大体结束,在测试等环节也遇到了一些麻烦,比如:eclipse缺少那个覆盖率的插件、Jprofiler不会用等等。好在互联网就是解决问题的宝库,在自己的努力下还是解决了许多问题。
总结下来就是:从不理解到了解,再到读懂,然后到实现经历了很多困难,好在都一一解决了。
9.相关仓库
1.JavaGuide:https://github.com/Snailclimb/JavaGuide
简介:Java学习+面试指南,一份涵盖大部分Java程序员所需要掌握的核心知识。适合各个阶段的同学阅读学习。
2.Heart-First-JavaWeb:https://github.com/skyline75489/Heart-First-JavaWeb
简介:一个走心的 Java Web 入门开发教程,面向新手友好,容易上手,同时没有废话,有很多实用的例子适合入门者学习。
3.Java:https://github.com/chenhaoxiang/Java
简介:Java的学习之路,学习JavaEE以及框架时候的一些项目,结合博客和源码,让你受益匪浅,适合Java初学者和刚入门开始学框架者。
4.hibernate-orm:https://github.com/hibernate/hibernate-orm
简介:Hibernate的核心对象/关系映射功能,适合进一步学习Hibernate框架。
5.SpringAll:https://github.com/wuyouzhuguli/SpringAll
简介:循序渐进,学习Spring Boot、Spring Boot & Shiro、Spring Cloud、Spring Security & Spring Security OAuth2,博客Spring系列源码,帮助初学者学习Spring。