寒假第二次作业-疫情统计
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/2020SpringW |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/2020SpringW/homework/10281 |
这个作业的目标 | 建立博客,美化博客,加入班级,编写博客(回顾初心,当下和未来,学习路线) |
作业正文 | https://www.cnblogs.com/KeVinZ2/p/12319055.html |
其他参考文献 | CSDN |
一、GitHub仓库地址:https://github.com/KevinZ2Z/InfectStatistic-main
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 50 | 65 |
Estimate | 估计这个任务需要多少时间 | 1120 | 1500 |
Development | 开发 | 920 | 1270 |
Analysis | 需求分析 (包括学习新技术) | 60 | 90 |
Design Spec | 生成设计文档 | 60 | 100 |
Design Review | 设计复审 | 40 | 60 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 40 | 60 |
Design | 具体设计 | 40 | 60 |
Coding | 具体编码 | 500 | 660 |
Code Review | 代码复审 | 60 | 60 |
Test | 测试(自我测试,修改代码,提交修改) | 120 | 180 |
Reporting | 报告 | 200 | 230 |
Test Report | 测试报告 | 120 | 140 |
Size Measurement | 计算工作量 | 20 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 60 | 70 |
合计 | 1170 | 1565 |
三、解题思路
分析题意可得,该题要求通过命令行输入参数,java主程序根据参数,读取相关文件内容,分析后输出到目标文件。因此整体分为三步:解析命令,读取文件,输出文件。
- 命令为list,且含有log,out,province,date,type等参数,可使用类记录相关参数是否存在,并记录该参数后的参数列表,如type后的sp,ip等。
- 解析完命令后,根据命令访问日志文件并记录数据,日志文件可能存在如下情况:
1、 新增 感染患者 n人
2、 新增 疑似患者 n人
3、 感染患者 流入 n人
4、 疑似患者 流入 n人
5、 死亡 n人
6、 治愈 n人
7、 疑似患者 确诊感染 n人
8、 排除 疑似患者 n人
观察可得,每行存在唯一关键词,如:新增、流入、死亡、治愈、确诊、排除,所以可以逐行读取文件并判断是否存在相应关键词,如:如果有新增,则再判断是否有“感染患者”,在对应省份的感染患者人数加上n人- 读取文件并记录后,再根据是否存在province或type参数,逐行输出对应数据。
四、设计实现过程
1、创建一个参数类(Parameter),用于记录参数名,参数是否存在,参数类型(参数后的参数列表为1个还是多个),参数后的参数列表,存在的参数。
- 在外部类中通过构造函数创建该类,对该类的参数名和参数类型进行赋值
- 通过set方法,对传递进来的命令进行解析,分析到与参数名一致的参数时,再根据参数类型对后续参数分析并赋值给参数列表,如type为多参数类型,则分析后续是否有ip,sp等参数,并用参数列表List类型记录
- 静态方法List get_file_list()根据log的文件路径,生成文件列表并返回
2、创建信息类(Info),用于记录全国每个省份的疫情情况。
- 类成员包括疫情信息(使用哈希表记录),最后需要输出的省份(ArrayList),最后需要输出的疫情类型(sp,ip等)
- add_province()方法用于添加最后需要输出的省份
- add_type()方法添加最后需要输出的类型
- Infected()方法对感染人数进行处理
- Suspected()方法对疑似感染人数进行处理
- Cured()方法对治愈人数进行处理
- Dead()方法对死亡人数进行处理
- Diagnosis()方法对确诊人数进行处理
- Exclude()方法对排除人数进行处理
- execute_out_file()方法用于最后输出到文件
3、创建命令类(Command),用于执行相应命令
- 类成员包括log参数类,date参数类,province参数类,type参数类,out参数类,Info类
- execute_command()方法解析并执行命令
- execute_date()执行date参数
- execute_type()执行dtype参数,将最后需要输出的类型添加进Info类的疫情类型成员中
- execute_province()执行province参数,将最后需要输出的省份添加进Info类的省份类型成员中
五、代码说明
1、InfectStatistic类(作为命令判断的第一步,仅解析判断命令是否为空或第一参数是否为“list”,若通过则创建command类,并调用execute_command()方法执行命令)
class InfectStatistic
{
public static void main(String[] args)
{
try
{
if (args.length == 0)
{
throw new MyException("不能输入空命令!");
}
else if (!args[0].equals("list"))
{
throw new MyException("请输入正确命令!");
}
else
{
Command command = new Command(args);
command.execute_command();
}
}
catch(MyException ex)
{
System.out.println(ex.getMessage());
}
}
}
2、参数类(Parameter类)(主要用于存储参数信息)
class Parameter
{
public String name;//参数名称
public boolean is_exist;//参数是否存在
public String param;//若type为1时的参数列表(如-datye的type为1,则param为2020-01-22)
public List param_list;//若type为2时的参数列表
public int type;//type为1则该参数后的参数列表数目为1个,type为2时参数列表为多个
private List has_param;//记录哪些参数为合法
Parameter(String name,int type)
{
this.name = name;
this.type = type;
is_exist = false;
param = "";
param_list = new ArrayList<>();
has_param = new ArrayList<>();
has_param.add("-log");
has_param.add("-out");
has_param.add("-date");
has_param.add("-type");
has_param.add("-province");
}
public void set(String[] args) throws MyException
{
if(type == 1)
{
int i;
for(i = 1;i get_file_list(String dir) throws MyException
{
File Dir = new File(dir);
if(!Dir.exists())
{
throw new MyException("路径不存在!");
}
String rule = "\\d{4}-\\d{2}-\\d{2}.log.txt";
List file_list = new ArrayList<>();
for(File file : Dir.listFiles())
{
if(file.getName().matches(rule))
{
file_list.add(file);
}
}
return file_list;
}
}
- 类成员应该设为private用get方法获取类成员,本次作业工程量不高,为方便固用public类
- set()方法用于设置参数相关信息
- get_file_list()方法根据路径读取正则表达式满足\d{4}-\d{2}-\d{2}.log.txt的文件,生成文件列表并返回
3、信息类(Info类)
public Map> info;//记录疫情信息
public List out_province;//记录最后需要输出的省份
public List out_type;//记录最后需要输出的类型
Info()//构造函数
{
out_province = new ArrayList<>();
out_province.add("全国");
out_type = new ArrayList<>();
info = new LinkedHashMap<>();
List new_data = new ArrayList<>();
new_data.add(0); //记录感染人数
new_data.add(0); //记录疑似感染人数
new_data.add(0); //记录愈人数
new_data.add(0); //记录死亡人数
info.put("全国",new ArrayList<>(new_data));
info.put("安徽",new ArrayList<>(new_data));
info.put("澳门",new ArrayList<>(new_data));
info.put("北京",new ArrayList<>(new_data));
info.put("重庆",new ArrayList<>(new_data));
info.put("福建",new ArrayList<>(new_data));
info.put("甘肃",new ArrayList<>(new_data));
info.put("广东",new ArrayList<>(new_data));
info.put("广西",new ArrayList<>(new_data));
info.put("贵州",new ArrayList<>(new_data));
info.put("河北",new ArrayList<>(new_data));
info.put("海南",new ArrayList<>(new_data));
info.put("河南",new ArrayList<>(new_data));
info.put("黑龙江",new ArrayList<>(new_data));
info.put("湖北",new ArrayList<>(new_data));
info.put("湖南",new ArrayList<>(new_data));
info.put("吉林",new ArrayList<>(new_data));
info.put("江苏",new ArrayList<>(new_data));
info.put("江西",new ArrayList<>(new_data));
info.put("辽宁",new ArrayList<>(new_data));
info.put("内蒙古",new ArrayList<>(new_data));
info.put("宁夏",new ArrayList<>(new_data));
info.put("青海",new ArrayList<>(new_data));
info.put("山东",new ArrayList<>(new_data));
info.put("山西",new ArrayList<>(new_data));
info.put("陕西",new ArrayList<>(new_data));
info.put("上海",new ArrayList<>(new_data));
info.put("四川",new ArrayList<>(new_data));
info.put("台湾",new ArrayList<>(new_data));
info.put("天津",new ArrayList<>(new_data));
info.put("香港",new ArrayList<>(new_data));
info.put("西藏",new ArrayList<>(new_data));
info.put("新疆",new ArrayList<>(new_data));
info.put("云南",new ArrayList<>(new_data));
info.put("浙江",new ArrayList<>(new_data));
}
- 初始化类成员,使用哈希表存储疫情信息,键值为省份名称,List列表0至3分别对应感染人数、疑似人数、治愈人数和死亡人数
public void Infected(String province,int Count)
{
List province_list = info.get(province);
List Country_list = info.get("全国");
int province_count = province_list.get(0);
province_count = province_count + Count;
int country_count = Country_list.get(0);
country_count = country_count + Count;
province_list.set(0,province_count);
Country_list.set(0,country_count);
}
- 参数1为省份名称,参数2为人数,该方法用于处理感染人数的增加,剩下疑似人数、治愈人数、死亡人数,由于方法类似就不一一放上来了
public void execute_out_file(String file_path) throws MyException
{
File info_file = new File(file_path);
FileWriter file_writer = null;
try
{
file_writer = new FileWriter(info_file);
if(!info_file.exists())
info_file.createNewFile();
for(String province : info.keySet())
{
if(!out_province.contains(province))
continue;
if(out_type == null || out_type.size() == 0)
{
file_writer.write(province + " " + "感染患者" + info.get(province).get(0).toString() + "人 ");
file_writer.write("疑似患者"+info.get(province).get(1).toString() + "人 ");
file_writer.write("治愈"+info.get(province).get(2).toString() + "人 ");
file_writer.write("死亡"+info.get(province).get(3).toString() + "人");
file_writer.write("\n");
}
else
{
file_writer.write(province + " ");
for(String type : out_type)
{
if(type.equals("ip"))
{
file_writer.write("感染患者" + info.get(province).get(0).toString() + "人 ");
}
if(type.equals("sp"))
file_writer.write("疑似患者" + info.get(province).get(1).toString() + "人 ");
if(type.equals("cure"))
file_writer.write("治愈" + info.get(province).get(2).toString() + "人 ");
if(type.equals("dead"))
file_writer.write("死亡" + info.get(province).get(3).toString() + "人 ");
}
file_writer.write("\n");
}
}
}
catch (Exception ex)
{
throw new MyException(ex.getMessage());
}
finally
{
try
{
if(file_writer != null)
file_writer.close();
}
catch (Exception ex)
{
throw new MyException(ex.getMessage());
}
}
}
}
*处理输出,根据需要输出的省份和疫情类型进行逐行输出至目标文件
4、Command类
private Parameter log = new Parameter("-log",1);
private Parameter out = new Parameter("-out",1);
private Parameter date = new Parameter("-date",1);
private Parameter province = new Parameter("-province",2);
private Parameter type = new Parameter("-type",2);
//存放疫情最新信息
private Info new_info;
//记录log参数的目录信息
private File log_dir;
public Command(String[] args) throws MyException
{
log.set(args);
out.set(args);
date.set(args);
province.set(args);
type.set(args);
new_info = new Info();
}
- 构造函数调用参数类的set方法对每个参数进行解析,记录是否存在及其参数列表
public void execute_command() throws MyException
{
if(!log.is_exist)
{
throw new MyException("必须输入log参数");
}
if(!out.is_exist)
{
throw new MyException("必须输入out参数");
}
if(date.is_exist && date.param == "")//若date后面无参数
{
throw new MyException("date后必须输入参数");
}
if(date.is_exist && date.param != "")//有date参数且date后参数不为空
{
List file_list = Parameter.get_file_list(log.param);
execute_date(date.param,file_list,date.is_exist);
}
if(!date.is_exist)
{
List file_list = Parameter.get_file_list(log.param);
execute_date(date.param,file_list,date.is_exist);
}
if(type.is_exist)
{
execute_type(type.param_list);
}
if(province.is_exist)
{
if(province.param_list ==null || province.param_list.size() == 0)
throw new MyException("-province后必须含有省份名称");
execute_province(province.param_list);
}
new_info.execute_out_file(out.param);
}
- 最终的命令执行方法,根据各参数是否存在及其参数列表执行相应的方法
private void execute_date(String date,List file_list,boolean exist) throws MyException
{
Date date_param;
BufferedReader reader = null;
DateFormat date_format = new SimpleDateFormat("yyyy-MM-dd");
try
{
date_param = date_format.parse(date);
Date date_now = date_format.parse(date_format.format(new Date()).toString());
if(date_param.compareTo(date_now) > 0)
throw new MyException("输入时间大于系统当前时间");
if(!exist)
date_param = date_now;
for(File file : file_list)
{
String data_row;
Date file_date = date_format.parse(file.getName().substring(0,file.getName().indexOf('.')));
if(file_date.compareTo(date_param) > 0)
continue;
reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
while((data_row = reader.readLine()) != null)
{
if(data_row.startsWith("//"))
continue;
String[] data = data_row.split(" ");
new_info.add_province(data[0]);
if(data_row.contains("新增"))
{
int count = Integer.parseInt(data[3].substring(0,data[3].length() - 1));
if(data[2].equals("感染患者"))
{
new_info.Infected(data[0],count);
}
if(data[2].equals("疑似患者"))
{
new_info.Suspected(data[0],count);
}
}
if(data_row.contains("流入"))
{
int count = Integer.parseInt(data[4].substring(0,data[4].length() - 1));
if(data[1].equals("感染患者"))
{
new_info.Inflow(data[0],data[3],count,0);
}
if(data[1].equals("疑似患者"))
{
new_info.Inflow(data[0],data[3],count,1);
}
}
if(data_row.contains("治愈"))
{
int count = Integer.parseInt(data[2].substring(0,data[2].length() - 1));
new_info.Cured(data[0],count);
}
if(data_row.contains("死亡"))
{
int count = Integer.parseInt(data[2].substring(0,data[2].length() - 1));
new_info.Dead(data[0],count);
}
if (data_row.contains("确诊感染"))
{
int count = Integer.parseInt(data[3].substring(0,data[3].length() - 1));
new_info.Diagnosis(data[0],count);
}
if (data_row.contains("排除"))
{
int count = Integer.parseInt(data[3].substring(0,data[3].length() - 1));
new_info.Exclude(data[0],count);
}
}
}
}
catch(Exception ex)
{
throw new MyException(ex.getMessage());
}
finally
{
try
{
if (reader != null)
{
reader.close();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
- 执行date参数
public void execute_type(List type_param) throws MyException
{
if(type_param == null || type_param.size() == 0)
return;
for(String type : type_param)
{
switch (type)
{
case "ip":
new_info.add_type("ip");
break;
case "sp":
new_info.add_type("sp");
break;
case "cure":
new_info.add_type("cure");
break;
case "dead":
new_info.add_type("dead");
break;
default:
throw new MyException("-type请输入正确参数(ip,sp,cure,dead)");
}
}
}
- 执行type参数,province参数执行方法类似
class MyException extends Exception //自定义异常类
{
public MyException()
{
super();
}
public MyException(String s)
{
super(s);
}
}
- 自定义异常类
六、单元测试
单元测试用的日志为作业提供的模板
public void test_InfectStatistic()
{
String[] x = new String [0];
System.out.println("测试用例1:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic2()
{
String[] x = new String [2];
x[0] = "java";
System.out.println("测试用例2:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic3()
{
String[] x = new String [6];
x[0] = "list";
x[1] = "-date";
x[2] = "-log";
x[3] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[4] = "-out";
x[5] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut1.txt";
System.out.println("测试用例3:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic4()
{
String[] x = new String [8];
x[0] = "list";
x[1] = "-date";
x[2] = "2020-1-22";
x[3] = "-log";
x[4] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[5] = "-out";
x[6] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut1.txt";
x[7] = "-province";
System.out.println("测试用例4:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic5()
{
String[] x = new String [7];
x[0] = "list";
x[1] = "-date";
x[2] = "2020-1-22";
x[3] = "-log";
x[4] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[5] = "-out";
x[6] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut1.txt";
System.out.println("测试用例5:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic6()
{
String[] x = new String [8];
x[0] = "list";
x[1] = "-date";
x[2] = "2020-1-22";
x[3] = "-log";
x[4] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[5] = "-out";
x[6] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut2.txt";
x[7] = "-type";
System.out.println("测试用例6:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic7()
{
String[] x = new String [9];
x[0] = "list";
x[1] = "-date";
x[2] = "2020-1-22";
x[3] = "-log";
x[4] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[5] = "-out";
x[6] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut3.txt";
x[7] = "-type";
x[8] = "sp";
System.out.println("测试用例7:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic8()
{
String[] x = new String [12];
x[0] = "list";
x[1] = "-date";
x[2] = "2020-1-22";
x[3] = "-log";
x[4] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[5] = "-out";
x[6] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut4.txt";
x[7] = "-type";
x[8] = "sp";
x[9] = "-province";
x[10] = "全国";
x[11] = "福建";
System.out.println("测试用例8:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic9()
{
String[] x = new String[10];
x[0] = "list";
x[1] = "-date";
x[2] = "2020-1-22";
x[3] = "-log";
x[4] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[5] = "-type";
x[6] = "sp";
x[7] = "-province";
x[8] = "全国";
x[9] = "福建";
System.out.println("测试用例9:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic10()
{
String[] x = new String[12];
x[0] = "list";
x[1] = "-date";
x[2] = "2020-1-22";
x[3] = "-log";
x[4] = "\\log\\";
x[5] = "-type";
x[6] = "sp";
x[7] = "-province";
x[8] = "全国";
x[9] = "福建";
x[10] = "-out";
x[11] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut5.txt";
System.out.println("测试用例10:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic11()
{
String[] x = new String[10];
x[0] = "list";
x[1] = "-log";
x[2] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[3] = "-type";
x[4] = "sp";
x[5] = "-province";
x[6] = "全国";
x[7] = "福建";
x[8] = "-out";
x[9] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut5.txt";
System.out.println("测试用例11:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic12()
{
String[] x = new String[12];
x[0] = "list";
x[1] = "-log";
x[2] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[3] = "-type";
x[4] = "sp";
x[5] = "-province";
x[6] = "全国";
x[7] = "福建";
x[8] = "-out";
x[9] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut5.txt";
x[10] = "-date";
x[11] = "2020-2-18";
System.out.println("测试用例12:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic13()
{
String[] x = new String[10];
x[0] = "list";
x[1] = "-log";
x[2] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[3] = "-province";
x[4] = "全国";
x[5] = "福建";
x[6] = "-out";
x[7] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut6.txt";
x[8] = "-date";
x[9] = "2020-1-23";
System.out.println("测试用例13:");
InfectStatistic.main(x);
}
@Test
public void test_InfectStatistic14()
{
String[] x = new String[10];
x[0] = "list";
x[1] = "-log";
x[2] = "F:\\GitHub\\InfectStatistic-main\\log\\";
x[3] = "-province";
x[4] = "全国";
x[5] = "福建";
x[6] = "-out";
x[7] = "F:\\GitHub\\InfectStatistic-main\\result\\ListOut7.txt";
x[8] = "-date";
x[9] = "2020-1-21";
System.out.println("测试用例14:");
InfectStatistic.main(x);
}
七、单元测试覆盖率和性能
1、单元测试覆盖率
2、性能
从测试结果看,程序大部分时间都花在了readLine()方法上,但是按行读取文件,是我处理文件的核心思想,在其他方面的优化也对于性能提升也并不明显,
所以在优化中遇到了难题,暂时无法解决。
八、代码规范链接:
https://github.com/KevinZ2Z/InfectStatistic-main/blob/master/221701337/codestyle.md
九、心路历程和收获
一开始阅读作业要求,看了一遍下来后,脑子里是一片空白的.
作业中包含了非常多没有学过的知识和相关工具,比如GitHub,单元测试等。
万事开头难,虽然一开始像个无头苍蝇什么都不懂,但是通过慢慢接触学习,并且和同学进行讨论之后也逐渐顺利了起来,
因为同学先一步开始,所以吸取了很多经验。在逐步学习的过程中也对软件开发和项目管理有了更深刻的认识。
- 完成一个项目不仅仅是编码那么简单,还需要需求分析、单元测试、设计、复审等,事先规划好项目时间分配,需求分析可以让编码速度大大缩短
- 学习到了单元测试这个概念,让我了解到了可以更好测试代码的方法,不再是自己简单设计几个用例然后套用在程序中
- 完成本次作业之前完全不了解JProfiler,通过学习知道了代码性能测试,关于这个工具的基础使用,但是对于这个工具还有很多方面需要学习
- 复习并巩固了Java相关知识
- 通过阅读邹欣老师的博客:工程师的能力评估和发展和《构建之法》使我对软件工程师和团队合作有了更深刻的认识,学会了如何衡量自己的能力,也明白了现在的自己还有很多不足,在以后的学习中我也会继续完善自己
十、技术路线图相关仓库
- JavaGuide(java学习加面试指南)
https://github.com/Snailclimb/JavaGuide
简介:一份涵盖大部分Java程序员所需要掌握的核心知识。- jobbole/awesome-java-cn
https://github.com/jobbole/awesome-java-cn
简介:Java资源大全中文版,包括开发库、开发工具、网站、博客、微信、微博等- qianguyihao/Web
https://github.com/qianguyihao/Web
简介:前端入门和进阶学习笔记,超详细的Web前端学习图文教程。从零开始学前端,做一名精致的前端工程师。- ljianshu/Blog
https://github.com/ljianshu/Blog
简介:关注基础知识,打造优质前端博客- doocs/advanced-java
https://github.com/doocs/advanced-java
简介:互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识