软工实践寒假作业(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 版)

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号