寒假作业(2/2)——疫情统计
这个作业属于哪个课程 | 2020春|W班 (福州大学) |
---|---|
这个作业的要求在哪里 | 寒假作业(2/2)——疫情统计 |
这个作业的目标 | Git命令、Github Desktop使用,开发一个疫情统计程序 |
作业正文 | 寒假作业(2/2) |
其他参考文献 | 无 |
Github仓库地址
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 60 |
Estimate | 估计这个任务需要多少时间 | 30 | 20 |
Development | 开发 | 1120 | 1126 |
Analysis | 需求分析 (包括学习新技术) | 300 | 330 |
Design Spec | 生成设计文档 | 40 | 60 |
Design Review | 设计复审 | 30 | 25 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 60 | 64 |
Design | 具体设计 | 120 | 125 |
Coding | 具体编码 | 480 | 400 |
Code Review | 代码复审 | 30 | 32 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 90 |
Reporting | 报告 | 420 | 642 |
Test Report | 测试报告 | 180 | 420 |
Size Measurement | 计算工作量 | 60 | 72 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 180 | 150 |
合计 | 1630 | 1848 |
我的解题思路
- 首先,看了好几遍作业的要求。
- 自己理解一下,就是:在命令行输入 $ java InfectStatistic list -date 2020-01-22 -log D:/log/ -out D:/output.txt
其中,java InfectStatistic表示执行主类InfectStatistic,list为命令,
程序从D:/log/目录下读取所有2020-01-22之前(包括2020-01-22)的日志文件,经过处理后将结果输出到D:/output.txt文件。 - 程序流程图
设计实现
类
Province
//保存排好序的各个省份
Parameters
//保存参数信息
Regular
//对应语句的正则表达式
Result
//一个省份对应一个Result
ResultList
//由Result组成
函数
parseOption
//解析命令行传入的参数
getLogFiles
//获得相关所有日志的文件路径
parseLog
//解析一篇日志
getIpResult
//使某省感染患者+n
getSpResult
//使某省疑似患者+n
getCureResult
//使某省治愈患者+n
getDeadResult
//使某省死亡患者+n
getIpMoveResult
//使省1感染患者+n,省2感染患者-n
getSpMoveResult
//使省1疑似患者+n,省2疑似患者-n
getSpToIpResult
//使某省疑似患者-n,感染患者+n
getSpClearResult
//使某省疑似患者-n
mergeList
//合并相同省份数据
outPut
//将结果输出到-out指定文件
关键代码
1. Province类
public static class Province{
public static String[] province= {"全国", "安徽", "澳门" ,"北京", "重庆", "福建","甘肃",
"广东", "广西", "贵州", "海南", "河北", "河南", "黑龙江", "湖北", "湖南", "吉林",
"江苏", "江西", "辽宁", "内蒙古", "宁夏", "青海", "山东", "山西", "陕西", "上海",
"四川", "台湾", "天津", "西藏", "香港", "新疆", "云南", "浙江"};
public static String getByIndex(int index){
return province[index];
}
}
2. Parameters类
static class Parameters {
String log;
String out;
String date;
ArrayList<String> type;
ArrayList<String> province;
String[] args = null;
String str1 = null;
String str2 = null;
String str3 = null;
int i = 0;
//仅为单元测试使用
public Parameters(){
log = null;
out = null;
date = null;
type = null;
province = null;
}
public Parameters(String log,String out,String date,ArrayList<String> type,ArrayList<String> province){
this.log = log;
this.out = out;
this.date = date;
this.type = type;
this.province = province;
}
public void setArgs(String[] args){
this.args = args;
}
public String getArgsString(){
String argsStr = "";
for (String arg:args
) {
if(!arg.equals(null))
argsStr += arg + " ";
}
return argsStr;
}
public String getParameterString() {
if(date != null) {
str1 = "log: " + log + " " + "out: " + out + " " + "date: " + date + " ";
}
else{
str1 = "log: " + log + " " + "out: " + out + " " + "date: ";
}
for(i = 0,str2 = "type: ";i < type.size();i++){
str2 += type.get(i).toString() + " ";
}
for(i = 0,str3 = "province: ";i < province.size();i++){
str3 += province.get(i).toString() + " ";
}
str1+=str2 + str3;
return str1;
}
}
3. Regular类
static class Regular{
String regularOne = "\\W+ 新增 感染患者 \\d+人";
String regularTwo = "\\W+ 新增 疑似患者 \\d+人";
String regularThree = "\\W+ 感染患者 流入 \\W+ \\d+人";
String regularFour = "\\W+ 疑似患者 流入 \\W+ \\d+人";
String regularFive = "\\W+ 死亡 \\d+人";
String regularSix = "\\W+ 治愈 \\d+人";
String regularSeven = "\\W+ 疑似患者 确诊感染 \\d+人";
String regularEight = "\\W+ 排除 疑似患者 \\d+人";
}
4. Result类
static class Result{
String province;
int ip;
int sp;
int cure;
int dead;
boolean isRefer;
public Result(){
this.province = null;
this.ip = 0;
this.sp = 0;
this.cure = 0;
this.dead = 0;
this.isRefer = false;
}
public void setProvince(String province){
this.province = province;
}
public void setIp(int ip){
this.ip += ip;
}
public void setSp(int sp){
this.sp += sp;
}
public void setCure(int cure){
this.cure += cure;
}
public void setDead(int dead){
this.dead += dead;
}
public void setRefer(boolean bool){
this.isRefer = bool;
}
//得到指定类型输出
public String getResultString() {
return province + " " + "感染患者" + ip + "人" + " " + "疑似患者" + sp + "人" + " " +"治愈" + cure + "人" + " " + "死亡" + dead + "人";
}
public String getAssignResultString(Parameters parameters){
String result = province + " ";
for (String type:parameters.type
) {
if(type.equals("ip")){
result += " " + "感染患者" + ip + "人";
}
else if(type.equals("sp")){
result += " " + "疑似患者" + sp + "人";
}
else if(type.equals("cure")){
result += " " + "治愈" + cure + "人";
}
else if(type.equals("dead")){
result += " " + "死亡" + dead + "人";
}
}
return result;
}
}
5. ResultList类
static class ResultList{
List<Result> resultList = new ArrayList<Result>();
ResultList(){
for(int i = 0;i < 35;i++){
Result result = new Result();
result.setProvince(Province.getByIndex(i));
resultList.add(result);
}
}
/**
*@描述 合并相同省份的数据
*@参数 List<Result> currentResultList,Parameters parameters
*@返回值 List<Result>
*@创建人 221701101林露
*@创建时间 2020/2/15
*/
public List<Result> mergeList(List<Result> currentResultList,Parameters parameters){
for (Result currentResult:currentResultList
) {
for(int i = 0;i < 35;i++ ){
if(currentResult.province.equals(resultList.get(i).province)){
resultList.get(i).setIp(currentResult.ip + resultList.get(i).ip);
resultList.get(i).setSp(currentResult.sp + resultList.get(i).sp);
resultList.get(i).setCure(currentResult.cure + resultList.get(i).cure);
resultList.get(i).setDead(currentResult.dead + resultList.get(i).dead);
resultList.get(i).setRefer(currentResult.isRefer||resultList.get(i).isRefer);
}
}
}
if(parameters.province.size() == 0)
resultList.get(0).setRefer(true);
else {
for (String province:parameters.province
) {
if(province.equals("全国"))
resultList.get(0).setRefer(true);
}
}
return resultList;
}
}
6. parseOption()函数
public static Parameters parseOptions(String[] args) {
Parameters parameters = new Parameters();
parameters.setArgs(args);
ArrayList<String> typeList = new ArrayList<>();
ArrayList<String> provinceList = new ArrayList<>();
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-log")) {
parameters.log = args[i + 1];
i++;
} else if (args[i].equals("-out")) {
parameters.out = args[i + 1];
i++;
} else if (args[i].equals("-date")) {
if (i == args.length - 1)
continue;
else {
if (args[i + 1].substring(0, 1).equals("-"))
continue;
else {
parameters.date = args[i + 1];
i++;
}
}
} else if (args[i].equals("-type")) {
for (int j = i+1; j < args.length; j++) {
if(!args[j].substring(0, 1).equals("-")) {
typeList.add(args[j]);
i++;
}
else
break;
}
} else if (args[i].equals("-province")) {
for (int j = i+1; j < args.length; j++) {
if(!args[j].substring(0, 1).equals("-")) {
provinceList.add(args[j]);
i++;
}
else
break;
}
}
}
parameters.type = typeList;
parameters.province = provinceList;
return parameters;
}
7. getLogFiles()函数
public static List<String> getLogFiles(Parameters parameters){
File file = new File(parameters.log);
File[] fileList = file.listFiles();
String fileName = null;
String latest = fileList[0].getName().substring(0,10);
List<String> logFiles = new ArrayList<String>();
try{
for (int i = 0; i < fileList.length; i++) {
fileName = fileList[i].getName();
if(fileName.substring(0,10).compareTo(latest) > 0)
latest = fileName.substring(0,10);
if(parameters.date != null){
if (fileName.substring(0,10).compareTo(parameters.date) <= 0) { //如果该文件的日期小于指定日期
logFiles.add(parameters.log + "\\" + fileName);
}
}
}
}catch(NullPointerException e){
e.printStackTrace();
}
if(parameters.date == null)
parameters.date = latest;
return logFiles;
}
8. parseLog()函数
public static List<Result> parseLog(String logPath,List<Result> resultList){// ,String date
Regular regular = new Regular();
List<Result> list = new ArrayList<Result>();
File file = new File(logPath);
if(file.isFile()){
BufferedReader reader = null;
try{
reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));
String currentLine = null;
while((currentLine = reader.readLine()) != null){
// 1、<省> 新增 感染患者 n人
if(currentLine.matches(regular.regularOne)){
getIpResult(currentLine,resultList);
}
// 2、<省> 新增 疑似患者 n人
else if(currentLine.matches(regular.regularTwo)){
getSpResult(currentLine,resultList);
}
//3、<省1> 感染患者 流入 <省2> n人
else if(currentLine.matches(regular.regularThree)){
getIpMoveResult(currentLine,resultList);
}
//4、<省1> 疑似患者 流入 <省2> n人
else if(currentLine.matches(regular.regularFour)){
getSpMoveResult(currentLine,resultList);
}
//5、<省> 死亡 n人
else if(currentLine.matches(regular.regularFive)){
getDeadResult(currentLine,resultList);
}
//6、<省> 治愈 n人
else if(currentLine.matches(regular.regularSix)){
getCureResult(currentLine,resultList);
}
//7、<省> 疑似患者 确诊感染 n人
else if(currentLine.matches(regular.regularSeven)){
getSpToIpResult(currentLine,resultList);
}
//8、<省> 排除 疑似患者 n人
else if(currentLine.matches(regular.regularEight)){
getSpClearResult(currentLine,resultList);
}
}
}catch (IOException e){
e.printStackTrace();
}
}
return list;
}
9. getIpResult()函数
其他 getSpResult()、getCureResult()、getDeadResult()、getIpMoveResult()、getSpMoveResult()、getSpToIpResult()、getSpClearResult()函数的代码也类似。
public static List<Result> getIpResult(String currentLine,List<Result> provincesResult){
Pattern pattern1 = Pattern.compile("(.*) 新增");
Pattern pattern2 = Pattern.compile("感染患者 (.*)人");
Matcher matcher1 = pattern1.matcher(currentLine);
Matcher matcher2 = pattern2.matcher(currentLine);
if(matcher1.find()&&matcher2.find()){
for (Result provinceResult:provincesResult
) {
if(provinceResult.province.equals(matcher1.group(1))) {
provinceResult.setIp(Integer.parseInt(matcher2.group(1)));
provinceResult.setRefer(true);
}
}
provincesResult.get(0).setIp(Integer.parseInt(matcher2.group(1)));
}
return provincesResult;
}
10. outPut()函数
public static void outPut(ResultList list,Parameters parameters) throws IOException {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(parameters.out)),"UTF-8"));
try {
for (Result result : list.resultList
) {
//没有指定省份
if(parameters.province.size() == 0){
list.resultList.get(0).setRefer(true);
//在日志中出现
if(result.isRefer == true) {
//没有指定类型
if(parameters.type.size() == 0)
bw.write(result.getResultString());
//指定类型
else
bw.write(result.getAssignResultString(parameters));
bw.write("\n");
}
else
continue;;
}
//指定省份
else{
for (String pro: parameters.province
) {
//找到相应省份
if(pro.equals(result.province)){
//没有指定类型
if(parameters.type.size() == 0)
bw.write(result.getResultString());
//指定类型
else
bw.write(result.getAssignResultString(parameters));
bw.write("\n");
}
}
}
}
bw.write("// 该文档并非真实数据,仅供测试使用\n");
bw.close();
}catch(IOException e){
e.printStackTrace();
}
}
关于单元测试
1. 解析日志函数测试(通过)
2. 新增感染患者函数测试(通过)
3.新增疑似患者函数测试(通过)
4.治愈患者函数测试(通过)
5.死亡患者函数测试(通过)
6.感染患者移动函数测试(通过)
7.疑似患者移动函数测试(通过)
8.疑似患者确诊函数测试(通过)
9.排除疑似患者函数测试(通过)
10.解析参数函数测试(通过)
11.main函数测试
12.测试用例
单元测试覆盖率优化/性能测试
我的代码规范
链接
心路历程与收获
虽然是一个简单的程序,但我只能说自己太菜了,只怪之前基础打的不够牢。除了认识JAVA,这次作业里的内容我真的……(都是什么鬼?)虽然一开始很艰难,但是最后也慢慢磨出来了,慢慢在黑暗之中看见了光明。这次的作业,我最大的收获就是get了单元测试!对我这种漏洞百出的代码简直就是仙丹啊!!只要试上几个测试用例就知道错在哪里了,特别是编程到最后,明明在IDEA上运行好好的,在命令行执行程序结果就不一样了,来几个测试用例…原来是编码问题啊…获益良多,以后必须养成写单元测试的好习惯。然后就是性能测试……什么东东?看来自己还是读书太少,只能说再接再厉吧!
WEB前端相关的5个仓库
1.VUE相关
vue-native-core
简介:Vue Native是一个使用JavaScript构建跨平台原生移动应用程序的框架继react-native之后,vue社区诞生了weex,之后又来个vue native
2.React相关
wired-elements
简介:一系列具有手绘外观的基本UI元素。这些可用于线框,模型,或只是有趣的手绘外观。
3.小程序相关
taro
简介:- 京东多端统一开发框架,支持用 React 的开发方式编写一次代码,生成能运行在微信小程序、H5、React Native 等的应用。
4.小程序相关
didi
简介:模仿滴滴打车微信小程序,持续更新中,有兴趣的小伙伴可以加入一起沟通交流噢。实现了主要的业务流程,数据封装和请求,本小程序仍会不断的更新...
5.桌面应用
proton-native
简介:有和react native一样的语法;可以与与现有的react redux库一起使用;跨平台;原生的组件,不再使用electron;与正常的node包兼容