寒假作业2

这个作业属于哪个课程 w班
这个作业要求在哪里 作业要求
这个作业的目标 回顾报考软件工程的初心,当初怎么看待软件工程和自己是如何学习的,并立下学习目标建立学习路线
作业正文 ....
其他参考文献 ...

一、Github仓库地址

本项目使用java语言开发

https://github.com/jokercan/InfectStatistic-main ->点击进入

二、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 25
Estimate 估计这个任务需要多少时间 30 20
Development 开发 1400 1550
Analysis 需求分析 (包括学习新技术) 50 50
Design Spec 生成设计文档 30 45
Design Review 设计复审 50 60
Coding Standard 代码规范 (为目前的开发制定合适的规范) 30 50
Design 具体设计 250 230
Coding 具体编码 500 630
Code Review 代码复审 40 50
Test 测试(自我测试,修改代码,提交修改) 60 90
Reporting 报告 120 150
Test Repor 测试报告 40 60
Size Measurement 计算工作量 30 35
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 20 40
合计 2680 3085

三、解题思路

1.什么是输入

  将题目看完之后能够发现存在有两个程序有两个输入的东西,一个是输入的日志内容,一个是命令行里面的参数。所以在程序里面要处理的就是这两个东西。

  • 输入的日志分析
      日志的文本内容是固定的形式。即为:

      <省> 新增 感染患者 n人
      <省> 新增 疑似患者 n人
      <省1> 感染患者 流入 <省2> n人
      <省1> 疑似患者 流入 <省2> n人
      <省> 死亡 n人
      <省> 治愈 n人
      <省> 疑似患者 确诊感染 n人
      <省> 排除 疑似患者 n人

  从日志的这个形式我们可以认识到,日志文件中可以通过正则表达式来抓取省份和人数信息,再通过不同的情况来做人员的计算处理。

  • 输入的命令行分析
      该程序是通过命令行参数进行运行的,命令的一个实例如下:

$ java InfectStatistic list -date 2020-01-22 -log D:/log/ -out D:/output.txt

  在这其中java InfecStatistic表示执行主类InfectStatistic,list为命令,-date和-out是参数,其后的是参数值
  题目也给出了list命令的参数支持

  • -log 指定日志目录的位置,该项必会附带,请直接使用传入的路径,而不是自己设置路径
  • -out 指定输出文件路径和文件名,该项必会附带,请直接使用传入的路径,而不是自己设置路径
  • -date 指定日期,不设置则默认为所提供日志最新的一天。你需要确保你处理了指定日期之前的所有log文件
  • -type 可选择[ip: infection patients 感染患者,sp: suspected patients 疑似患者,cure:治愈 ,dead:死亡患者],使用缩写选择,如 -type ip 表示只列出感染患者的情况,-type sp cure则会按顺序【sp, cure】列出疑似患者和治愈患者的情况,不指定该项默认会列出所有情况。
  • -province 指定列出的省,如-province 福建,则只列出福建,-province 全国 浙江则只会列出全国、浙江

  所以在程序的一开始我们就需要检测语句是否是正确的。可以知道-log参数我们需要它指定的路径下面一定要具有相应的日志文件。-out所指示的文件是能够创建的,-date,-province,-type的参数值是固定值或者固定形式的。根据这些参数和参数值从而筛选得到每个省份的各种人的数值。

2.什么是输出

  程序输出的内容最终存放于用户所给的文件。输出也有特定的形式。例如:

全国 感染患者22人 疑似患者25人 治愈10人 死亡2人
福建 感染患者2人 疑似患者5人 治愈0人 死亡0人
浙江 感染患者3人 疑似患者5人 治愈2人 死亡1人
// 该文档并非真实数据,仅供测试使用

所以这种简单的形式可以用pojo类来完成。

1jCuHP.png

四、设计实践的过程

1.正则表达式->REUtil

  保存需要的正则表达式。设置函数传入相应的字符串对比输出相等与否。eg:

public boolean checkProvinces(String a){
String province = "全国|北京|天津|上海|重庆|河北|山西|辽宁|吉林|黑龙江|江苏|浙江|安徽|福建|江西|山东|河南|湖北|湖南|广东|海南|四川|贵林|云南|陕西|甘肃|青海|台湾|内蒙古|广西|西藏|宁夏|新疆|香港|澳门";
return Pattern.matches(province,a);
}

1jppvR.png

2.语句判断->CommandJudge

  判断输入的命令行是否是正确的.基本规则如下

  • 命令行的第一个参数是list
  • list后面紧跟的是参数
  • 参数之间必须存在间隔
  • 参数必须是-log,-out,-date,-province,-type里面的一种
  • 参数-log,-out必须存在

  在这之后还有参数值的校验,如果能够通过检验才运行后面的程序。

1jSApj.png

3.pojo类->Province

  保存省份的ip,sp,cure,dead的人数。供文件的输入输出使用。

4.具体执行->ListCommand

  执行命令语句。

1jCE1H.png

五、代码说明

1.REUtil

  判断是否符合-type的参数值
public boolean checkType(String a) {return Pattern.matches("ip|sp|cure|dead",a);}
  判断参数是否正确
public boolean checkParamter(String a){...}
  判断日期是否符合形式
public boolean checkDate(String a){}{...}
  判断输入省份是否符合要求
public boolean checkProvinces(String a){...}
  判断是否为所需要的日志形式
public boolean checkLogFile(String a){...}
  判断文件行里面的内容

    public int txtType(String a){
        String log1 = "(\\S+) 新增 感染患者 (\\d+)人";
        String log2 = "(\\S+) 新增 疑似患者 (\\d+)人";
        String log3 = "(\\S+) 感染患者 流入 (\\S+) (\\d+)人";
        String log4 = "(\\S+) 疑似患者 流入 (\\S+) (\\d+)人";
        String log5 = "(\\S+) 死亡 (\\d+)人";
        String log6 = "(\\S+) 治愈 (\\d+)人";
        String log7 = "(\\S+) 疑似患者 确诊感染 (\\d+)人";
        String log8 = "(\\S+) 排除 疑似患者 (\\d+)人";

        if (Pattern.matches(log1 , a))
            return 1;
        if (Pattern.matches(log2 , a))
            return 2;
        if (Pattern.matches(log3 , a))
            return 3;
        if (Pattern.matches(log4 , a))
            return 4;
        if (Pattern.matches(log5 , a))
            return 5;
        if (Pattern.matches(log6 , a))
            return 6;
        if (Pattern.matches(log7 , a))
            return 7;
        if (Pattern.matches(log8 , a))
            return 8;
        return 0;
    }

2.CommandJudge

  判断语句输入的正确性。不正确返回0

public int judge(String[] args){
        java.util.List<Integer> integers = new LinkedList<Integer>();//存储参数的下标
        if (!args[0].equals("list") && !args[0].equals("help") && !args[0].equals("present")){
            System.out.println("Command " + args[0] + " not found");
            return 0;
        }else{
            if(args[0].equals("list)){
                ...//判断语句是否能够执行
            }
            if(args[0].equals("help")){
                ...//判断新功能help命令是否能执行
            }
            if(args[0].equals("present"){
                ...//判断新功能present是否能够执行
            }
        }
}

  寻找参数的下标值
public int foundSpecialIndex(String[] args , String special) {...}
  是否存在参数
public boolean foundSpecial(String[] args , String special) {...}
  获得命令行中的省份信息
public LinkedList<String> foundProvinces(String[] args) {...}
  获得命令行中的类型信息
public LinkedList<String> foundType(String[] args) {...}

3.ListCommand

  对日志文件的名字进行排序,以达到顺序读取的效果
public void sort(String[] filesName) {...}
  将从日志文件读取到的一行字符进行匹配,并修改相应省对应的Province类

public void getProvinceInfect(String input , LinkedList<Province> provinceList)  {
...
        String[] provinces = input.split(" ");
        String s = provinces[provinces.length - 1].replaceAll(regEx , "");
        int peopleNumber = 0;
                if (!s.isEmpty())
        peopleNumber = Integer.parseInt(s);
        int inputType = new REUtil().txtType(input);
                if (inputType == 1) {
    provinceList.get(hashMap.get(provinces[0])).setIp(provinceList.get(hashMap.get(provinces[0])).getIp() + peopleNumber);
        }
        ...
...
}

执行语句并输出

public void Command(String[] args) throws IOException{  
    LinkedList<Province> provinceList = new LinkedList<Province>();
    ...
    String logPath = args[new CommandJudge().foundSpecialIndex(args , "-log") + 1];
    File file = new File(logPath);
    File[] files = file.listFiles();
    String[] filesName = new String[files.length];
    for (int j = 0 ; j < files.length ; j++){
            filesName[j] = files[j].getName();
        }
    ...
     if (new CommandJudge().foundSpecial(args , "-date")){
            for (int j = 0 ; j < filesName.length ; j++){
                if (args[new CommandJudge().foundSpecialIndex(args , "-date")+1].compareTo(filesName[j]) > 0)
                    realfilesName.add(filesName[j]);
            }
        }else {
            for (int j = 0 ; j < filesName.length ; j++){
                realfilesName.add(filesName[j]);
            }
        }
        ...
        if (new CommandJudge().foundSpecial(args , "-province")){
            infectProvinces.clear();
            LinkedList<String> list = new CommandJudge().foundProvinces(args);
            System.out.println(list);
            for (int k = 0 ; k < list.size() ; k++){
//                System.out.println(list.get(k));
                int num = hashMap.get(list.get(k));
                infectProvinces.add(num);
            }
        }else {
            infectProvinces.clear();
            for (int k = 0 ; k < provinceList.size() ; k++){
                infectProvinces.add(k);
            }
        }
        ...
        if (new CommandJudge().foundSpecial(args , "-type")){
            LinkedList<String> types = new CommandJudge().foundType(args);
            for (int k = 0 ; k < infectProvinces.size() ; k++){
                String putIn = provinceList.get(infectProvinces.get(k)).getName();
                for (int p = 0 ; p < types.size() ; p++){
                    if (types.get(p).equals("ip"))
                        putIn += (" 感染患者" + provinceList.get(infectProvinces.get(k)).getIp() + "人");
                    if (types.get(p).equals("sp"))
                        putIn += (" 疑似患者" + provinceList.get(infectProvinces.get(k)).getSp() + "人");
                    if (types.get(p).equals("cure"))
                        putIn += (" 治愈" + provinceList.get(infectProvinces.get(k)).getCure() + "人");
                    if (types.get(p).equals("dead"))
                        putIn += (" 死亡" + provinceList.get(infectProvinces.get(k)).getDead() + "人");
                }
                //识别系统。不同系统换行符不一样
                String osName = System.getProperty("os.name");
                if (osName.startsWith("Mac os")) {
                    bw.write(putIn + "\n");
                }else if(osName.startsWith("Windows")){
                    bw.write(putIn + "\r\n");
                }else {
                    bw.write(putIn + "\r");
                }
            }
        }else{
            for (int k = 0 ; k < infectProvinces.size() ; k++){
                String putIn = provinceList.get(k).getName() + " 感染患者" + provinceList.get(k).getSp() + "人 疑似患者" + provinceList.get(k).getIp() + "人 治愈" +
                        provinceList.get(k).getCure() + "人 死亡" + provinceList.get(k).getDead() + "人";
                String osName = System.getProperty("os.name");
                if (osName.startsWith("Mac os")) {
                    bw.write(putIn + "\n");
                }else if(osName.startsWith("Windows")){
                    bw.write(putIn + "\r\n");
                }else {
                    bw.write(putIn + "\r");
                }
            }
        }
        ...
}

六、单元测试截图和描述

  该程序实现主要由三个类构成,即REUtil,CommadnJudge,ListCommand所完成。所以单元测试在这三个类中展开。
使用maven项目进行管理。测试的依赖为

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

1.REUtil

  • 函数checkParameter用来检测参数是否符合要求
    @Test
   public void checkParameter() {
//        检测正常的参数能够使用
       assertTrue(new REUtil().checkParameter("-date"));
       assertTrue(new REUtil().checkParameter("-province"));
       assertTrue(new REUtil().checkParameter("-type"));
//        参数错误不能通过
       assertFalse(new REUtil().checkParameter("list"));
   }
  • 函数checkDate检测日期,日期形式必须为YYYY-mm-dd,且日期必须存在
    @Test
    public void checkDate() {
        assertTrue(new REUtil().checkDate("2020-01-01"));
        assertFalse(new REUtil().checkDate("2020-13-01"));
        assertTrue(new REUtil().checkDate("2020-02-29"));
        assertFalse(new REUtil().checkDate("2020-02-30"));
        assertTrue(new REUtil().checkDate("1998-12-14"));
        assertFalse(new REUtil().checkDate("1998-12-00"));
    }
  • 函数checkProvinces检测省份,省份必须是那三十一个里面的一个,外加一个全国
    @Test
    public void checkProvinces() {
        assertTrue(new REUtil().checkProvinces("全国"));
        assertTrue(new REUtil().checkProvinces("福建"));
        assertFalse(new REUtil().checkProvinces("美国"));
        assertTrue(new REUtil().checkProvinces("北京"));
        assertFalse(new REUtil().checkProvinces("sadasd"));
    }
  • 函数checkLogFile检测日志文件,日志文件需要符合YYYY-mm-dd.log.txt形式,并且日期要存在
    @Test
    public void checkLogFile() {
        assertTrue(new REUtil().checkLogFile("2020-01-02.log.txt"));
        assertFalse(new REUtil().checkLogFile("2020-01-02.log"));
        assertFalse(new REUtil().checkLogFile("2020-01-02.txt"));
        assertFalse(new REUtil().checkLogFile("2020-13-01.log.txt"));
        assertFalse(new REUtil().checkLogFile("2020-02-30.log.txt"));
    }
  • 函数checkType检测类型,类型一定是ip,sp,cure,dead里面的一种
    @Test
    public void checkType() {
        assertTrue(new REUtil().checkType("ip"));
        assertTrue(new REUtil().checkType("sp"));
        assertTrue(new REUtil().checkType("cure"));
        assertTrue(new REUtil().checkType("dead"));
        assertFalse(new REUtil().checkType("福建"));
        assertFalse(new REUtil().checkType("北京"));
        assertFalse(new REUtil().checkType("2020-13-01"));
        assertFalse(new REUtil().checkType("-date"));
    }
  • 函数txtType检测字符串内容,筛选日志文件里内容
    @Test
    public void txtType() {
//        文档要求形式才能够通过
        assertTrue(new REUtil().txtType("福建 新增 感染患者 2人") != 0);
        assertTrue(new REUtil().txtType("// 该文档并非真实数据,仅供测试使用") == 0);
        assertTrue(new REUtil().txtType("湖北 感染患者 流入 福建 2人") != 0);
        assertTrue(new REUtil().txtType("// 湖北 感染患者15-2-1-2=10人 疑似患者20-3-2=15人 治愈2人 死亡1人") == 0);
        assertTrue(new REUtil().txtType("湖北 排除 疑似患者 2人") != 0);
        assertTrue(new REUtil().txtType("当日情况:list") == 0);
    }
&nbsp;&nbsp;测试结果:

1jedEj.png

2.CommandJudge

  • 函数judge用来接收命令,判断命令是否是正确的,能够使用解析的。其余的函数是在judge中有被调用,所以不再测试。
    • e1-e7是部分错误的命令形式,还有部分没有列出来s1-s6是正确的。错误理由在代码注释中有解释。
    @Test
    public void judge() {
    

// 命令不完整
String e1 = "list";
// -log 不存在日志文件
String e2 = "list -log /Users/a/file -out /Users/a/Desktop/infect.txt";
// -log -out需要同时存在
String e3 = "list -out /Users/a/Desktop/infect.txt";
// -province的参数值不存在
String e4 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect1.txt -province 美国";
// -type的参数值错误
String e5 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect1.txt -province 福建 -type a";
// -date的参数形式错误
String e6 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect1.txt -province 福建 -type ip -date 2020-20-20";
// 参数重复
String e7 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect1.txt -province 福建 -type ip -type ip";
String s1 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect1.txt";
String s2 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect2.txt -type ip";
String s3 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect3.txt -province 福建 北京";
String s4 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect4.txt -date 2020-01-24";
String s5 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect5.txt -type ip";
String s6 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect6.txt -type ip sp -province 福建 北京 -date 2020-02-11";

    String[] ee1 = e1.split(" ");
    String[] ee2 = e2.split(" ");
    String[] ee3 = e3.split(" ");
    String[] ee4 = e4.split(" ");
    String[] ee5 = e5.split(" ");
    String[] ee6 = e6.split(" ");
    String[] ee7 = e7.split(" ");
    String[] ss1 = s1.split(" ");
    String[] ss2 = s2.split(" ");
    String[] ss3 = s3.split(" ");
    String[] ss4 = s4.split(" ");
    String[] ss5 = s5.split(" ");
    String[] ss6 = s6.split(" ");

    assertTrue(new CommandJudge().judge(ee1) == 0);
    assertTrue(new CommandJudge().judge(ee2) == 0);
    assertTrue(new CommandJudge().judge(ee3) == 0);
    assertTrue(new CommandJudge().judge(ee4) == 0);
    assertTrue(new CommandJudge().judge(ee5) == 0);
    assertTrue(new CommandJudge().judge(ee6) == 0);
    assertTrue(new CommandJudge().judge(ee7) == 0);

    assertTrue(new CommandJudge().judge(ss1) != 0);
    assertTrue(new CommandJudge().judge(ss2) != 0);
    assertTrue(new CommandJudge().judge(ss3) != 0);
    assertTrue(new CommandJudge().judge(ss4) != 0);
    assertTrue(new CommandJudge().judge(ss5) != 0);
    assertTrue(new CommandJudge().judge(ss6) != 0);
}
```

    &nbsp;&nbsp;测试结果,即命令行错误的报错信息:

1j1Gn0.png

3.ListCommandTest

  • 主要函数为command()。同上并利用上面的s1-s6来检测输出文件内容是否正确。代码如下
    @Test
    public void command() throws IOException {
        String s1 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect1.txt";
        String s2 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect2.txt -type ip";
        String s3 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect3.txt -province 福建 北京";
        String s4 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect4.txt -date 2020-01-24";
        String s5 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect5.txt -type ip";
        String s6 = "list -log /Users/a/Desktop/Infectlog -out /Users/a/Desktop/infect6.txt -type ip sp -province 福建 北京 -date 2020-02-11";

        String[] ss1 = s1.split(" ");
        String[] ss2 = s2.split(" ");
        String[] ss3 = s3.split(" ");
        String[] ss4 = s4.split(" ");
        String[] ss5 = s5.split(" ");
        String[] ss6 = s6.split(" ");

        new ListCommand().Command(ss1);
        new ListCommand().Command(ss2);
        new ListCommand().Command(ss3);
        new ListCommand().Command(ss4);
        new ListCommand().Command(ss5);
        new ListCommand().Command(ss6);
    }

1jY1eI.png
1jYMyd.png
1jYeJO.png
1jYJFf.png
1jYuSe.png
1jYYY8.png

七、单元测试覆盖优化和性能测试

  • 单元测试覆盖率
    1jdJxO.png
  • 性能测试:使用JProfiler分析
    1jdtMD.png

程序存在较多的循环判断,但还未有较好的解决办法。

八、代码规范

https://github.com/jokercan/InfectStatistic-main/blob/master/221701431/codestyle.md ->点击进入

九、心路历程与收获

每次进行一项作业或者任务的时候都会发现很多自己的不足,虽然这次作业不难,但还是让自己知道了一些知识点的缺失,比如正则表达式,还有性能优化的具体办法。程序也同样暴露了一些问题,过于依赖if-else形式,循环。事先设计的时候也遗漏了一些东西,只能边写边找补。暴露了不足才能更好的学习吧。

写完程序发现都是在用if/else的连环写法🐶
实在是好丑好丑,所以上网查询了一下解决办法。
发现有三种可以进行一定的替换:枚举,工厂模式,策略模式
下次一定下次一定。

十、技术相关的五个仓库

题目意思

java爬虫框架它具有简单的API,可快速上手,模块化的结构,可轻松扩展,且提供提供多线程和分布式支持

用动画的形式呈现解LeetCode题目的思路

Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。

优秀的Java博客系统

十一、新命令

  • help:显示所有命令的用法

eg. java InfectStatistic help

1vmH81.png

  • present:参数-out必须存在

eg. java InfectStatisticc present -out /Users/a/Desktop/a.txt

1x6LiF.png

posted @ 2020-02-14 23:18  会飞的大野鸡  阅读(334)  评论(4编辑  收藏  举报