软工实践寒假作业(2/2)
这个作业属于哪个课程 | 2020春丨S班 (福州大学) |
---|---|
这个作业要求在哪里 | 要求 |
这个作业的目标 | 开发疫情统计程序,学习GitHub |
作业正文 | 软工实践寒假作业(2/2) |
其他参考文献 | Java菜鸟教程,助教资料等 |
1、GitHub仓库地址
https://github.com/rzm-A/InfectStatistic-main
2、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30min | 30min |
Estimate | 估计这个任务需要多少时间 | 30min | 30min |
Development | 开发 | 60min | 60min |
Analysis | 需求分析 (包括学习新技术) | 200min | 200min |
Design Spec | 生成设计文档 | 60min | 60min |
Design Review | 设计复审 | 100min | 100min |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 60min | 60min |
Design | 具体设计 | 30min | 30min |
Coding | 具体编码 | 300min | 300min |
Code Review | 代码复审 | 200min | 200min |
Test | 测试(自我测试,修改代码,提交修改) | 200min | 200min |
Reporting | 报告 | 30min | 30min |
Test Repor | 测试报告 | 30min | 30min |
Size Measurement | 计算工作量 | 30min | 30min |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 150min | 160min |
合计 | 1510min | 1520min |
3、解题思路描述
程序主要功能:命令行参数处理、日志文件处理、输出结果
- 命令行参数处理
$ java InfectStatistic list -date 2020-01-22 -log D:/log/ -out D:/output.txt
list命令 支持以下命令行参数:- -log 指定日志目录的位置,该项必会附带,请直接使用传入的路径,而不是自己设置路径
- -out 指定输出文件路径和文件名,该项必会附带,请直接使用传入的路径,而不是自己设置路径
- -date 指定日期,不设置则默认为所提供日志最新的一天。你需要确保你处理了指定日期以及之前的所有log文件
- -type 可选择[ip: infection patients 感染患者,sp: suspected patients 疑似患者,cure:治愈 ,dead:死亡患者],使用缩写选择,如 -type ip 表示只列出感染患者的情况,-type sp cure则会按顺序【sp, cure】列出疑似患者和治愈患者的情况,不指定该项默认会列出所有情况。
- -province 指定列出的省,如-province 福建,则只列出福建,-province 全国 浙江则只会列出全国、浙江
对读取的参数进行检验,并且使用类InfectStatistic的成员变量保存部分参数以便后续调用。
- 日志文件处理
读取log指定的日志文件,根据日志统计全国及各省的感染患者、疑似患者、治愈和死亡人数
日志数据形式:- <省> 新增 感染患者 n人
- <省> 新增 疑似患者 n人
- <省1> 感染患者 流入 <省2> n人
- <省1> 疑似患者 流入 <省2> n人
- <省> 疑似患者 确诊感染 n人
- <省> 死亡 n人
- <省> 治愈 n人
- <省> 排除 疑似患者 n人
关于文件读取,所有日期先于date的都需读取处理,因为按日期处理数据,在计算过程可能出现负数
- 输出结果
将处理结果输出至out指定的输出文件,根据type与province参数选择部分数据进行输出,采用数据结构HashMap
4、设计实现过程
5、代码说明
- 命令行参数处理
//验证命令行传入参数的正误,初始化文件路径等
public static boolean Verify_Init_args(String[] args_) throws IOException {
if(args_[0].equals("list")){
for (int i = 1; i < args_.length; i++) {
i++;
switch (args_[i-1]) {
case "-date":
date_p = new String(args_[i]);
break;
case "-log":
log_p = args_[i];
File file = new File(log_p);
if (!file.exists()) {
System.out.print("-log错误!--");
return false;
}
break;
case "-out":
out_p = args_[i];
break;
case "-type":
AllType = true;
for(int j = 0;!args_[i].startsWith("-");j++,i++) {
if(!typeMap.containsKey(args_[i])) {
System.out.print("-type错误!--");
return false;
}
else
type_p[j] = typeMap.get(args_[i]);
}
i--;
break;
case "-province":
AllProvince = false;
for(int j = 0;i < args_.length && !args_[i].startsWith("-");j++,i++){
if(provinceList.contains(args_[i])) {
province_p.add(args_[i]);
}
else
System.out.print("-province错误!--");
}
i--;
break;
default:
return false;
}
}
return true;
}
return false;
}
- 日志文件处理
主要处理步骤:选取符合格式的.txt日志,打开日志逐行处理文件
//验证文件是否存在,不存在返回false,存在则读取处理文件数据返回true
@SuppressWarnings("null")
public boolean Read_Deal_File() throws IOException {
boolean b = false;
String filename;
String[] filedate;
String[] name = new String[100];
int[] num = new int[100];
int i,j;
File file = new File(log_p);
File[] tempList = file.listFiles();
//选取符合命名的文件
for(i=0,j = 0;i < tempList.length;i++) {
filename = new String(tempList[i].getName());
filedate = filename.split("\\.");
boolean b1,b2,b3,b4;
if(filedate[0].length() != 10)
continue;
if(filedate[0].charAt(4) != '-' || filedate[0].charAt(7) != '-')
continue;
b1 = Date_Isdit(filedate[0],0,1);
b2 = Date_Isdit(filedate[0],2,3);
b3 = Date_Isdit(filedate[0],5,6);
b4 = Date_Isdit(filedate[0],8,9);
if(b1 && b2 && b3 && b4 && filedate[0] !=null) {
name[j] = filedate[0];
num[j++] = i;
}
}
//读取处理log.txt文件
for(i = 0;i < j;i++) {
if(date_p.compareTo(name[i]) >= 0) {
BufferedReader br = null;
String dates = null;
br = new BufferedReader(new InputStreamReader(new FileInputStream(tempList[num[i]].toString()), "UTF-8"));
while ((dates = br.readLine()) != null){
Deal_Dates_Line(dates);
}
b = true;
br.close();
}
}
//对每个日志文件,统计全国信息
map.put(provinceList.get(0), new Province(provinceList.get(0)));
for (i = 0; i < provinceList.size(); i ++ ){
if (map.get(provinceList.get(i)) != null)
map.get(provinceList.get(0)).Statistics(map.get(provinceList.get(i)));
}
return b;
}
//检验是否是数字
private boolean Date_Isdit(String str,int num1,int num2) {
if(!Character.isDigit(str.charAt(num1)) || !Character.isDigit(str.charAt(num2))) {
return false;
}
return true;
}
//处理文件数据(单行)
private void Deal_Dates_Line(String dates) throws IOException {
String[] date = dates.split(" ");
if (map.get(date[0]) == null)
map.put(date[0], new Province(date[0]));
else if (date[0].equals("//"))
return;
switch(date[1])
{
case "新增":
if (date[2].equals("感染患者"))
map.get(date[0]).add_Ip(date[3]);
else
map.get(date[0]).add_Sp(date[3]);
break;
case "感染患者":
if (map.get(date[3]) == null)
map.put(date[3], new Province(date[3]));
map.get(date[3]).add_Ip(date[4]);
map.get(date[0]).reduce_Ip(date[4]);
break;
case "疑似患者":
if (date[2].equals("流入")){
if (map.get(date[3]) == null)
map.put(date[3], new Province(date[3]));
map.get(date[3]).add_Sp(date[4]);
map.get(date[0]).reduce_Sp(date[4]);
}
else{
map.get(date[0]).add_Ip(date[3]);
map.get(date[0]).reduce_Sp(date[3]);
}
break;
case "死亡":
map.get(date[0]).reduce_Ip(date[2]);
map.get(date[0]).dead(date[2]);
break;
case "治愈":
map.get(date[0]).reduce_Ip(date[2]);
map.get(date[0]).cure(date[2]);
break;
case "排除":
map.get(date[0]).reduce_Sp(date[3]);
break;
default:
break;
}
}
- 输出结果
//根据要求生成输出文件
public void Output_File() throws IOException {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(out_p), "UTF-8"));
//无-province参数,默认输出所有省情况
if (AllProvince)
{
for (int i = 0;i < provinceList.size();i++)
if (map.get(provinceList.get(i)) != null)
map.get(provinceList.get(i)).output(AllType,type_p ,bw);
}
else
{
for (int i = 0; i < provinceList.size(); i ++ )
if (province_p.contains(provinceList.get(i)))
map.get(provinceList.get(i)).output(AllType,type_p ,bw);
}
bw.write("// 该文档并非真实数据,仅供测试使用");
bw.close();
}
- 其他
Class Province 保存一个省的具体数据,提供根据文件数据进行修改的方法
//某个省份的具体数据
class Province {
//省份名称
private String provincename;
//疑似患者人数
private int sp;
//感染患者人数
private int ip;
//治愈人数
private int cure;
//死亡人数
private int dead;
//构造函数
public Province(String provincename){
this.provincename = provincename;
sp = 0;
ip = 0;
cure = 0;
dead = 0;
}
//感染患者增加
public void add_Ip(String str){
ip += Integer.parseInt(str.substring(0, str.length()-1));
}
//疑似患者增加
public void add_Sp(String str){
sp += Integer.parseInt(str.substring(0, str.length()-1));
}
//感染患者减少
public void reduce_Ip(String str){
ip -= Integer.parseInt(str.substring(0, str.length()-1));
}
//疑似患者减少
public void reduce_Sp(String str){
sp -= Integer.parseInt(str.substring(0, str.length()-1));
}
//死亡
public void dead(String str){
dead += Integer.parseInt(str.substring(0, str.length()-1));
}
//治愈
public void cure(String str){
cure += Integer.parseInt(str.substring(0, str.length()-1));
}
//统计全国数据
public void Statistics(Province p){
this.ip += p.ip;
this.sp += p.sp;
this.cure += p.cure;
this.dead += p.dead;
}
//输出指定省的信息
public void output(boolean alltype, String[] type, BufferedWriter bw) throws IOException{
//无-type参数,默认输出所有type类型
if (alltype){
bw.write(provincename+ " 感染患者 " +ip+ "人 疑似患者 " +sp+ "人 治愈 "
+cure+ "人 死亡 " +dead+ "人");
bw.newLine();
}
else
{
bw.write(provincename);
for (int i = 0; i < type.length; i ++ ){
bw.write(" " +type[i]);
switch (type[i])
{
case "感染患者":
bw.write(ip+ "人");
break;
case "疑似患者":
bw.write(sp+ "人");
break;
case "治愈":
bw.write(cure+ "人");
break;
case "死亡":
bw.write(dead+ "人");
break;
default:
break;
}
}
bw.newLine();
}
}
}
6、单元测试截图和描述
- 命令行参数处理
便于测试,这边直接在main()中传入命令行参数,并使用System.out.println()查看
//main()方法中
String[] aa= {"list", "-date","2020-01-27","-log" ,"C:\\Users\\东伯\\Desktop\\123","-out","D:\\ouput.txt","-type","ip","cure","-province","全国","福建","湖北"};
if (!Verify_Init_args(aa)) {
System.out.println("参数错误 ");
return ;
}
//Verify_Init_args()方法中,在返回结果前填加以下代码,其他参数也可如此检验
System.out.println("date = " +date_p);
运行结果:
- 日志文件处理
//Read_Deal_File()方法中
System.out.println("循环处理log.txt");
for(i = 0;i < j;i++) {
if(date_p.compareTo(name[i]) >= 0) {
System.out.println(i+ " 文件名:" +name[i]);
...
}
}
运行结果:
- 输出结果
运行结果
注意,若不指定type则输出全部类型,若不指定province则输出全国与全部省
7、单元测试覆盖率优化和性能测试,性能优化截图和描述
- 单元测试覆盖率
- 性能测试
- 性能优化
- 文件名检查:选取符合要求的函数写的过于粗糙简陋。
- ...
8、代码规范的链接
9、心路历程与收获
9.1 心路历程
乍一看,这啥呀,完全看不懂!然后慢慢的分析研究,感觉代码部分还是相对比较简单的,麻烦的是博客的编写。
9.2 收获
- 巩固了GitHub知识
- 复习了Java知识
- 学会了eclipse单元测试的方法和性能测试方法
- ...
10、 技术路线图相关的5个仓库
1、Java
链接:https://github.com/rzm-A/Java
简介:涵盖Java基础知识,详细讲解了Java编程中常用的API
2、JAVAWeb-Project
链接:https://github.com/rzm-A/JAVAWeb-Project
简介:初学JAVA-WEB开发的小项目💖
3、Linux
链接:https://github.com/rzm-A/linux
简介:Linux kernel source tree
4、Linux-Tutorial
链接:https://github.com/rzm-A/Linux-Tutorial
简介:Java 程序员眼中的 Linux
5、tldr.linux.cn
链接:https://github.com/rzm-A/tldr.linux.cn
简介:Linux 命令快速查询(Linux 小程序 Web 版)