这个作业属于哪个课程 | 2020软工|S班 |
这个作业要求在哪里 | 软工实践寒假作业(2/2) |
这个作业的目标 | 设计、开发一个疫情统计的程序、学习对程序的优化、学习GitHub的使用、PSP(个人软件开发流程)的学习使用、《构建之法》的学习 |
作业正文 | 作业正文 |
其他参考文献 | CSDN |
1.github仓库地址
我的github仓库地址
2.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 40 | 45 |
Estimate | 估计这个任务需要多少时间 | 5 | 5 |
Development | 开发 | 200 | 180 |
Analysis | 需求分析 (包括学习新技术) | 160 | 120 |
Design Spec | 生成设计文档 | 80 | 55 |
Design Review | 设计复审 | 30 | 35 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 150 | 150 |
Design | 具体设计 | 20 | 20 |
Coding | 具体编码 | 200 | 300 |
Code Review | 代码复审 | 60 | 70 |
Test | 测试(自我测试,修改代码,提交修改) | 340 | 270 |
Reporting | 报告 | 150 | 100 |
Test Repor | 测试报告 | 20 | 20 |
Size Measurement | 计算工作量 | 45 | 30 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1530 | 1430 |
3.我的解题思路描述
- 浏览了一遍作业要求之后,自己总结了一下整个作业的大概要求。在log文件里有在每一天某个省份的感染情况,我们需要整合这过往几天的某个省所有感染情况然后在在输出在输出文件中
- 而且有一个问题我之前没见过,那就是如何处理cmd命令行,之前一直没有搞懂main函数里的string[] args是个什么用的,csdn中查了一些资料后,我知道了这个是main函数运行的初始参数,那这不就是我没作业的“处理命令行”的要求吗
4.实现过程
4-1思维导图
4-2用到的类
Province
4-3实现该项目的流程
- 1st-处理命令行
- 2st-分行读取所有log文件
- 3st-处理log文件信息
4st-输出到指定文件
5.代码说明。展示出项目关键代码,并解释思路。
5-1.读取命令
5-1-1.分析命令含义
- log 指定日志目录的位置,该项必会附带,请直接使用传入的路径,而不是自己设置路径
- out 指定输出文件路径和文件名,该项必会附带,请直接使用传入的路径,而不是自己设置路径
- date 指定日期,不设置则默认为所提供日志最新的一天。你需要确保你处理了指定日期以及之前的所有log文件
- type 可选择[ip: infection patients 感染患者,sp: suspected patients 疑似患者,cure:治愈 ,dead:死亡患者],使用缩写选择,如 -type ip 表示只列出感染患者的情况,-type sp cure则会按顺序【sp, cure】列出疑似患者和治愈患者的情况,不指定该项默认会列出所有情况。
province 指定列出的省,如-province 福建,则只列出福建,-province 全国 浙江则只会列出全国、浙江
5-1-2.如何存储命令
- 我们知道cmd命令行长得长得像这样“参数名A 参数值1 参数值2 参数名B 参数值1 参数值2 ... 参数名C 参数值1 参数值2”这样
- 所谓存储命令,我们只要在这个一大串字符串的cmd命令当中,提取出pathString(log文件位置)、outString(输出文件位置)、等等参数值
- 那么怎么提取呢,我知道-log、-date、-out这三个参数名就只有1个参数值,遍历cmd命令行字符串是把对应的参数名的下标往下+1就好了
- 对于有多个参数值的参数名,那么下标要一直往下+1,我们只要确保下标不超出界限,并且遇到新的参数名是就停止+1
- 代码如下
//处理命令行 public static void DealCmd(String[] s) { String[] cmdString=new String[s.length-1]; int indexOfCmdString[]= {-1,-1,-1,-1,-1}; //获取cmd命令行中的命令(包括参数名+参数值),并去掉list for(int i=0;i<cmdString.length;i++){ cmdString[i]=s[i+1]; } //eg、indexOfCmdString[3]=8,表示在cmdString下标为8的字符串, //这个字符串在hashmap中的value为3,即key为"-type" for(int i=0;i<cmdString.length;i++){ if(map.containsKey(cmdString[i])) indexOfCmdString[map.get(cmdString[i])]=i; } //接下来开始处理各个参数名后面的参数值 for(int i=0;i<indexOfCmdString.length;i++){ if(indexOfCmdString[i]!=-1){ if(i==0) { //-log String pathStringReplace=cmdString[indexOfCmdString[i]+1]; pathString=pathStringReplace.replace('/','\\'); } else if(i==1) { //-out outPathString=cmdString[indexOfCmdString[i]+1]; } else if(i==2) { //-date dateString=cmdString[indexOfCmdString[i]+1]; } else if(i==3) { //-type String[] ans=new String[10]; int cnt=0; for(int j=indexOfCmdString[i]+1;j<cmdString.length&&!map.containsKey(cmdString[j]);j++) ans[cnt++] = cmdString[j]; typeString=ans; } else if(i==4) { //-province String[] ans=new String[25]; int cnt=0; for(int j=indexOfCmdString[i]+1;j<cmdString.length&&!map.containsKey(cmdString[j]);j++) ans[cnt++] = cmdString[j]; provinceString=ans; } } } for(int i=0;i<typeString.length;i++) //存储需要输出的某个情况,如ip、sp、等等 typeCmdMap.put(typeString[i],i); for(int i=0;i<provinceString.length;i++) //存储需要输出的省份 provinceCmdMap.put(provinceString[i],i); }
5-2.读取log文件信息
- 把-log和-date的参数值结合一下,可以得到在某个目录里的所有某个文件,这文件就是我们要统计的最后一天的log文件
- 把log下所有的log文件名(包括路径)一字符串的形式读到ArrayList中
- 遍历files(这个files是一个ArrayList
),把在最后一天log文件之前的log文件按行读取的到ans(这个ans
是ArrayList< String>)中 - 代码如下
//处理log文件信息 public static ArrayList<String> ReadLog(String pathString,String dateString) { ArrayList<String> ans=new ArrayList<>(); //返回按行读取的内容 List<String> filesName=new ArrayList<>(); filesName=GetFiles(pathString); for(int i=0;i<filesName.size();i++) { try { StringBuilder filePathAndNameSB=new StringBuilder(); //这里是将cmd命令里的-date参数和-log参数结合起来 filePathAndNameSB.append(pathString); //形成一个用于判断的条件 filePathAndNameSB.append(dateString); //eg.E:/log/2020-01-23.log.txt filePathAndNameSB.append(".log.txt"); // String filePathAndName=filePathAndNameSB.toString(); // if(filesName.get(i).compareTo(filePathAndName)<=0) //比较在cmd命令行指定目录下的文件日期是否早于 { //cmd命令指定的日期 File f = new File(filesName.get(i)); if(f.isFile()&&f.exists()) { InputStreamReader read = new InputStreamReader(new FileInputStream(f),"utf-8"); BufferedReader reader=new BufferedReader(read); String line; while ((line = reader.readLine()) != null) ans.add(line); reader.close(); read.close(); } } } catch (IOException e) { e.printStackTrace(); } } //删除txt中的注释 Iterator<String> iterator = ans.iterator(); while (iterator.hasNext()) { String s1=iterator.next(); String s2=s1.substring(0,1); if (s2.equals("/")) iterator.remove();//使用迭代器的删除方法删除 } return ans; }
5-3.处理log文件信息
- 大致浏览下log文件的类容,如果每行按空格分开的话,按数量分可以分为三类,如“湖北 死亡 1人”=数量为3、“福建 新增 感染患者 2人”=数量为4、“湖北 疑似患者 流入 福建 3人”=数量为5这三类
- 首先创建一个hashmap,用Integer和String构成一个键值对,其中String为provinceName
- 把之前按行读取的ans按空格开成一个新的字符串数组conString
- 对于“数量为3”的这一类,遍历hashmap,conString[0]是否存在于hashmap中,然后对dead[i]、cure[i]、ip[i]进行加减操作(其中i对应于hashmap中的provinceName)
- 对于“数量为4”的这一类,先判断第二个字符串是“新增”还是“排除”,“新增”里再判断第三个字符串是“感染患者”还是“疑似患者”,然后对dead[i]、cure[i]、ip[i]、sp[i]进行加减操作(其中i对应于hashmap中的provinceName)
- 对于“数量为5”的这一类, 直接判断第二个就行了
- 代码如下
//输出到指定文件 public void Out(String outPathString,String[] pString,HashMap<String,Integer> provinceCmdMap) { for(int i=0;i<32;i++) { if(provinceCmdMap.containsKey(pString[i])) { String st=pString[i]+" "; // 判断需要输出哪些情况,如ip、sp、等等 if(typeCmdMap.containsKey(tString[0])) // st+="感染患者"+ip[i]+"人 "; // if(typeCmdMap.containsKey(tString[1])) // st+="疑似患者"+sp[i]+"人 "; // if(typeCmdMap.containsKey(tString[2])) // st+="治愈"+cure[i]+"人 "; // if(typeCmdMap.containsKey(tString[3])) // st+="死亡"+dead[i]+"人 "; // st+="\n"; // try { File file = new File(outPathString); if(!file.exists()){ file.createNewFile(); } FileWriter fileWriter = new FileWriter(file.getAbsoluteFile(),true); BufferedWriter bw = new BufferedWriter(fileWriter); bw.write(st); bw.close(); } catch(IOException e) { e.printStackTrace(); } } } }
5-4.输出到指定文件
- 针对-type参数名,在处理cmd命令行的时候,就已经用typeCmdMap的hashmap把-type的参数值存好,这样直接看typeCmdMap里有没有,有的话就直接输出,没有的话就跳过,不输出
- 针对-province参数名,也是一样的,用provinceCmdMap的hashmap把-province的参数值存好,然后看跳不跳过输出
- 代码如下
//输出到指定文件 public void Out(String outPathString,String[] pString,HashMap<String,Integer> provinceCmdMap) { for(int i=0;i<32;i++) { if(provinceCmdMap.containsKey(pString[i])) { String st=pString[i]+" "; // 判断需要输出哪些情况,如ip、sp、等等 if(typeCmdMap.containsKey(tString[0])) // st+="感染患者"+ip[i]+"人 "; // if(typeCmdMap.containsKey(tString[1])) // st+="疑似患者"+sp[i]+"人 "; // if(typeCmdMap.containsKey(tString[2])) // st+="治愈"+cure[i]+"人 "; // if(typeCmdMap.containsKey(tString[3])) // st+="死亡"+dead[i]+"人 "; // st+="\n"; // try { File file = new File(outPathString); if(!file.exists()){ file.createNewFile(); } FileWriter fileWriter = new FileWriter(file.getAbsoluteFile(),true); BufferedWriter bw = new BufferedWriter(fileWriter); bw.write(st); bw.close(); } catch(IOException e) { e.printStackTrace(); } } } }
6.单元测试截图和描述。
6-1province参数测试
- 测试命令:provin参数值就只包含“全国”
- 我的结果
- 与真实结果对比
- 测试命令:provin参数值就包含“全国+福建+湖北”
- 我的结果
- 与真实结果对比
- 测试命令:provin参数值就包含“全国+福建+湖北+安徽+北京”
- 我的结果
与真实结果对比
6-2out参数测试
- 测试命令:-out的参数值由以开是的“E:\out.txt”变成了“E:\out111.txt”
我的结果
6-3date参数测试
- 测试命令:-date的参数值由以开是的“2020-01-22”变成了“2020-01-23”
- 我的结果
- 与真实结果对比
- 测试命令:-date的参数值由以开是的“2020-01-23”变成了“2020-01-27”
- 我的结果
与真实结果对比
6-4type参数测试
- 测试命令:-type参数值就只有“ip”
- 我的结果
- 测试命令:-type参数值为“ip+sp”
- 我的结果
- 测试命令:-type参数值为“ip+sp+cure”
- 我的结果
- 测试命令:-type参数值为“ip+sp+cure+dead”
我的结果
6-5log参数测试
- 测试命令:-log的参数值由以开是的“E:\log”变成了“E:\homework\log”
我的结果
6-6输出排序验证
- 测试命令:假设我-province的参数为“全国+福建+湖北+安徽+北京+广西+广东+黑龙江”,输出时将这些省份排序
我的结果
7.单元测试覆盖率优化和性能测试,性能优化截图和描述。
7-1优化前
- 优化前我的单元覆盖率
- 这是我优化之前处理log文件信息的函数
- 优化前我的cpu使用情况
优化前我的heap使用情况
7-2优化后
- 优化后我的单元覆盖率
- 这是我优化后处理log文件信息的函数
- 优化后我的cpu使用情况
优化后我的heap使用情况
7-3优化方法
7-3-1优化1
- 由于优化之前,我直接在main()函数里处理cmd信息,这样感觉不是很方便,于是我把处理cmd信息的封装成一个函数,这样更加方便
+我的代码如下
//处理命令行 public static void DealCmd(String[] s) { String[] cmdString=new String[s.length-1]; int indexOfCmdString[]= {-1,-1,-1,-1,-1}; //获取cmd命令行中的命令(包括参数名+参数值),并去掉list for(int i=0;i<cmdString.length;i++){ cmdString[i]=s[i+1]; } //eg、indexOfCmdString[3]=8,表示在cmdString下标为8的字符串, //这个字符串在hashmap中的value为3,即key为"-type" for(int i=0;i<cmdString.length;i++){ if(map.containsKey(cmdString[i])) indexOfCmdString[map.get(cmdString[i])]=i; } //接下来开始处理各个参数名后面的参数值 for(int i=0;i<indexOfCmdString.length;i++){ if(indexOfCmdString[i]!=-1){ if(i==0) { //-log String pathStringReplace=cmdString[indexOfCmdString[i]+1]; pathString=pathStringReplace.replace('/','\\'); } else if(i==1) { //-out outPathString=cmdString[indexOfCmdString[i]+1]; } else if(i==2) { //-date dateString=cmdString[indexOfCmdString[i]+1]; } else if(i==3) { //-type String[] ans=new String[10]; int cnt=0; for(int j=indexOfCmdString[i]+1;j<cmdString.length&&!map.containsKey(cmdString[j]);j++) ans[cnt++] = cmdString[j]; typeString=ans; } else if(i==4) { //-province String[] ans=new String[25]; int cnt=0; for(int j=indexOfCmdString[i]+1;j<cmdString.length&&!map.containsKey(cmdString[j]);j++) ans[cnt++] = cmdString[j]; provinceString=ans; } } } for(int i=0;i<typeString.length;i++) //存储需要输出的某个情况,如ip、sp、等等 typeCmdMap.put(typeString[i],i); for(int i=0;i<provinceString.length;i++) //存储需要输出的省份 provinceCmdMap.put(provinceString[i],i); }
- 我还优化了一个方法,就是,一开始我本来打算构造一个province类,然后使用类型为province的数组,后来感觉太浪费空间了,于是我就只构造一个类,类里有ip[i]、sp[i]等等元素,例如ip[0]就表示为全国的感染人数,例如ip[1]就表示为安徽省的感染人数。
+我的代码如下
class InfectStatistic { String[] provinceName=new String[32]; //省份名称 Integer[] ip=new Integer[32]; //感染患者 Integer[] sp=new Integer[32]; //疑似患者 Integer[] cure=new Integer[32]; //治愈 Integer[] dead=new Integer[32]; //死亡 ... }
8.给出你的代码规范的链接,即你的仓库中的codestyle.md
9.结合在构建之法中学习到的相关内容,撰写解决项目的心路历程与收获。
- 读完了《构建之法》前三章,我觉得对未来的道路有了一定预先的认识,就知道了“软件=程序+软件工程”,但是之前,我只知道软件为程序,所以书中涉及一些理论,或多或少对我未来的生产工作会起到指导作用。
- 我意识到一个项目的完成,不仅仅是实施编码的过程,而打代码之外的设计也一样重要,就好比这次作业的需求分析,对后期的的代码开发速度提升特别快,应为有一个方向然我们去实施
- 这次作业也收获了许多,就比如PSP、单元测试、覆盖率等等新的知识,这些有一部分都是之前了解过、但不怎么使用的,通过这次的学习,回顾了Java代码的编写,进一步了解了GitHub、markdown的一些使用技巧,收获真的很多
10.在github上寻找你在第一次作业中技术路线图相关的5个仓库,star并fork,在博客中提供名称、链接、简介(简介30字左右)
- 仓库地址:学习仓库1链接
仓库说明:这个仓库用python语言实现了绝大部分算法,主要是用于教学目的,因此效率稍微低于工业界。
- 仓库地址:学习仓库2链接
仓库说明:该仓库介绍了他学习算法详细过程,以及积累的经验
- 仓库地址:学习仓库3链接
仓库说明:该仓库也是介绍了他学习算法详细过程,以及积累的经验
- 仓库地址:学习仓库4链接
仓库说明:这是在leetcode上刷题的题解,以及思想方法的介绍
- 仓库地址:学习仓库5链接
仓库说明:这也是在leetcode上刷题的一些题解,以及思想方法的介绍
来源:https://www.cnblogs.com/liuchenghua/p/12312160.html