软工实践寒假作业(2/2)
这个作业属于哪个课程 | 2020春|S班(福州大学) |
---|---|
这个作业的要求在哪里 | 寒假作业(2/2) |
这个作业的目标 | 疫情统计 |
作业正文 | Wzzzq的作业(2/2) |
其他参考文献 | 百度、CSDN博客、b站 |
一、GitHub仓库
作业的主仓库:https://github.com/kofyou/InfectStatistic-main/pulls
我的Github仓库:https://github.com/WZQ233/InfectStatistic-main
二、PSP表格
PSP各个阶段 | Personal Software Proces Stages | 预计耗时(分钟) | 实践耗时 |
---|---|---|---|
Planning | 计划 | 30 | 30 |
Estimate | 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 1810 | 2240 |
Analysis | 需求分析(包括学习新技术) | 800 | 980 |
Design Spec | 生成设计文档 | 40 | 40 |
Design Review | 设计复审 | 30 | 30 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 60 | 80 |
Design | 具体设计 | 80 | 100 |
Coding | 具体编码 | 500 | 580 |
Code Review | 代码复审 | 80 | 80 |
Test | 测试(自我测试,修改代码,提交修改) | 300 | 350 |
Reporting | 报告 | 470 | 510 |
Test Report | 测试报告 | 60 | 80 |
Size Measurement | 计算工作量 | 60 | 50 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 350 | 380 |
合计 | 2310 | 2780 |
三、解题思路
思考
- 通过浏览题目可知需要通过命令行对指定文件夹下的日志文件进行处理,对全国和各省在某日的感染情况进行统计,后生成
output.txt
文件。 - 题目的输入为命令行参数及日志文件,输出为
output.txt
文件。 - 大致思路为根据所给日志文件路径读取日志文件并处理,并输出至指定的文件中,且根据其他参数来调整输出的内容。
- 在读取参数和日志文件时应当进行适当的非法检测,并抛出异常输出错误原因结束程序。
资料
- 大致阅读班级附录所给的学习资源,对于要使用到的部分仔细阅读。
- 根据所需要的技术在网上查阅相关教程,如在博客园上学习各种参数处理,哔哩哔哩上学习GitHub及GithubDesktop的使用等。
四、设计思路
编写一个异常类和主类来实现该程序,通过主类中的各种函数来实现题目所需功能,在出现错误时,异常类报错并结束程序。先通过inspectParameter()对命令行的参数进行检验,如果合法则调用execLog()函数对日志文件进行读取,execLog()函数中调用execFile()函数和outputFile()来保存数据和输出文件。其中用List来记录type和province参数的输入情况,用LinkedHashMap来存储日志文件中的数据。与此同时通过异常类来捕获异常并结束程序。
五、代码说明
InfectStatistic
主类说明
主类中主要变量及用途
public String logPath; //保存日志路径
public String outPath; //保存输出路径
public String dateStr;
boolean typeIsExist;
boolean provinceIsExist;
public List<String> types = new ArrayList<String>();
public List<String> provinces = new ArrayList<String>();
public String[] args; //保存命令
public String[] typeStr = {"ip","sp","crue","dead"}; //保存类型
public String[] provinceStr = {"全国", "安徽", "澳门" ,"北京", "重庆", "福建","甘肃",
"广东", "广西", "贵州", "海南", "河北", "河南", "黑龙江", "湖北", "湖南", "吉林",
"江苏", "江西", "辽宁", "内蒙古", "宁夏", "青海", "山东", "山西", "陕西", "上海",
"四川", "台湾", "天津", "西藏", "香港", "新疆", "云南", "浙江"}; //保存省份
public LinkedHashMap<String,Integer> ip = new LinkedHashMap<String,Integer>(); //保存各省的感染患者人数
public LinkedHashMap<String,Integer> sp = new LinkedHashMap<String,Integer>(); //保存各省的疑似患者人数
public LinkedHashMap<String,Integer> cure = new LinkedHashMap<String,Integer>(); //保存各省的治愈人数
public LinkedHashMap<String,Integer> dead = new LinkedHashMap<String,Integer>(); //保存各省的死亡人数
public String addIp = "\\s*\\S+ 新增 感染患者 \\d+人\\s*";
public String addSp = "\\s*\\S+ 新增 疑似患者 \\d+人\\s*";
public String inflowIp = "\\s*\\S+ 感染患者 流入 \\S+ \\d+人\\s*";
public String inflowSp = "\\s*\\S+ 疑似患者 流入 \\S+ \\d+人\\s*";
public String addDead = "\\s*\\S+ 死亡 \\d+人\\s*";
public String addCure = "\\s*\\S+ 治愈 \\d+人\\s*";
public String spToIp = "\\s*\\S+ 疑似患者 确诊感染 \\d+人\\s*";
public String reduceSp = "\\s*\\S+ 排除 疑似患者 \\d+人\\s*";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); //设定日期格式
Date d = new Date(System.currentTimeMillis());
public String date = dateFormat.format(d);
inspectParameter(String [] argsStr)
方法
inspectParameter(String [] argsStr)
方法可对命令行得参数进行检测,若参数合法则返回true
,不合法则返回false
.
public boolean inspectParameter(String [] argsStr) {
int j;
args = argsStr;
if(args.length == 0) {
System.out.print("No parameters entered.");
return false;
}
if(!argsStr[0].equals("list")) {
System.out.println("Command line format error.");
return false;
}
for(j = 1;j < argsStr.length;j++) {
switch(argsStr[j]) {
case "-log":
j = inspectLogPath(++j);
if(j == -1) {
System.out.println("Command line format error.('-log'parameter)");
return false;
}
else{
logPath = argsStr[j];
break;
}
case "-date":
j = inspectDate(++j);
if(j == -1) {
System.out.println("Command line format error.('-date'parameter)");
return false;
}
else{
dateStr = args[j];
break;
}
case "-out":
j = inspectOutPath(++j);
if(j == -1) {
System.out.println("Command line format error.('-outputFile'parameter)");
return false;
}
else{
outPath = argsStr[j];
break;
}
case "-type":
j = inspectType(++j);
if(j == -1) {
System.out.println("Command line format error.('-type'parameter)");
return false;
}
else
break;
case "-province":
j = inspectProvince(++j);
if(j == -1) {
System.out.println("Command line format error.('-province'parameter)");
return false;
}
else
break;
default:
System.out.println("Unknown error.");
return false;
}
}
if (types.isEmpty()) {
types.add("ip");
types.add("sp");
types.add("cure");
types.add("dead");
}
return true;
}`
execLog()
方法
调用execLog()
方法可以对从参数中获取得路径下的日志文件进行处理,并根据参数情况来保存数据及输出指定文件
public void execLog() throws Exception{
File f = new File(logPath);
String[] logFiles = f.list();
int l = logFiles.length;
List<String> legalFiles = new ArrayList<String>();
for(int i = 0;i < l;i++) {
String fileDate = logFiles[i].substring(0, 10);
String suffix = logFiles[i].substring(logFiles[i].lastIndexOf(".") + 1);
if(suffix.matches("txt") && dateStr.compareTo(fileDate) >=0)
legalFiles.add(logFiles[i]);
}
l = legalFiles.size();
if(l == 0)
throw new IllegalException("Error, no legal log file exists in the log directory");
logFiles = new String[l];
legalFiles.toArray(logFiles);
Arrays.sort(logFiles);
for(int i = 0;i < l;i++) {
execFile(logPath + "/" + logFiles[i]);
}
int ipSum = 0;
int spSum = 0;
int cureSum = 0;
int deadSum = 0;
for(Integer i : ip.values())
ipSum += i;
ip.put("全国", ipSum);
for(Integer i : sp.values())
spSum += i;
sp.put("全国", spSum);
for(Integer i : cure.values())
cureSum += i;
cure.put("全国", cureSum);
for(Integer i : dead.values())
deadSum += i;
dead.put("全国", deadSum);
FileOutputStream outFile = new FileOutputStream(outPath);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outFile, "utf-8"));
if (typeIsExist) {
if (!provinceIsExist) {
List<String> list = new ArrayList<String>();
int size = provinceStr.length;
for (int i = 0; i < size; i++) {
if (ip.get(provinceStr[i]) == 0 && sp.get(provinceStr[i]) == 0
&& cure.get(provinceStr[i]) == 0 && dead.get(provinceStr[i]) == 0)
continue;
else
list.add(provinceStr[i]);
}
outputFile(writer, list);
}
else {
outputFile(writer, provinces);
}
}
else {
if (provinceIsExist) {
for (String province : provinces) {
writer.write(province + " 感染患者" + ip.get(province) + "人 疑似患者" + sp.get(province) + "人 治愈"
+ cure.get(province) + "人 死亡" + dead.get(province) + "人\n");
}
}
else {
Integer[] ipProvincesAmount = new Integer[ip.size()];
ip.values().toArray(ipProvincesAmount);
Integer[] spProvincesAmount = new Integer[sp.size()];
sp.values().toArray(spProvincesAmount);
Integer[] cureProvincesAmount = new Integer[cure.size()];
cure.values().toArray(cureProvincesAmount);
Integer[] deadProvincesAmount = new Integer[dead.size()];
dead.values().toArray(deadProvincesAmount);
int size = provinceStr.length;
for (int i = 0; i < size; i++) {
if (ipProvincesAmount[i] == 0 && spProvincesAmount[i] == 0 && cureProvincesAmount[i] == 0
&& deadProvincesAmount[i] == 0)
continue;
else
writer.write(
provinceStr[i] + " 感染患者" + ipProvincesAmount[i] + "人 疑似患者" + spProvincesAmount[i]
+ "人 治愈" + cureProvincesAmount[i] + "人 死亡" + deadProvincesAmount[i] + "人\n");
}
}
}
writer.write("//该文档并非真实数据,仅供测试使用");
writer.close();
}
六、单元测试
七、单元测试覆盖率优化和性能测试
八、代码规范
详见Github仓库:codestyle.md
九、心路历程与收获
总的说来,一开始看到题目还是感觉啥也不懂,不知道到底要写个什么程序。但是反复阅读再加上和同学讨论后,大概明白大概要完成什么样的一个程序,感觉和Linux课写的脚本作业有点像,相比会复杂好多。比起看一遍就想到思路然后很快就完成的大佬,我只能扣666了。虽然跌跌撞撞,中途也快要面临崩溃,总归还是写完。不得不说deadline是第一生产力作业得早点开始写,不然真的赶不过来。通过这次作业我也学到了很多知识,比如GitHub desktop的使用,单元测试等,此外还复习了Java的相关知识,又是收获满满身心疲惫的一次作业。希望自己早日养成良好学习习惯,不再咸鱼。
十、技术路线相关的五个仓库
1.spring-projects/spring-loaded:spring开源的一个热替换插件,运行时监听class文件的变更并执行transform。
GitHub:https://github.com/spring-projects/spring-loaded
2.biezhi/blade:一款轻量级、高性能、简洁优雅的MVC框架。
GitHub:https://github.com/lets-blade/blade
3.spring-boot(Star:26k)
Github:https://github.com/spring-projects/spring-boot
4.基于SSM框架的前后端分离设计完整仿天猫网站服务器端源码。
Github:https://github.com/Liweimin0512/MMall_JAVA
5.mall学习教程,架构、业务、技术要点全方位解析。
Github:https://github.com/macrozheng/mall-learning