×

我的个人资料

绿箭无糖 / 男
除了必要的学习还喜欢关注时事、打打游戏
当前就读于福州大学
没有什么其他的了,去我的博客看看吧!

☰ 欢迎来到绿箭无糖的博客☰

软件实践寒假作业(2/2)

这个作业属于哪个课程 福大20春软工S班
这个作业要求在哪里 软工实践寒假作业(2/2)
这个作业的目标 新型冠状病毒疫情统计
作业正文 221701135王泽宇作业(2/2)
其他参考文献 《Java程序设计》

1. Github仓库

  个人代码见Github仓库:2217wang/Infectstatistic-main

2. PSP表格

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

3. 解题思路描述

  由作业要求可知:先读取日志文件统计疫情情况,再根据命令行参数输入的选项要求,输出相应的文档。

  • 程序语言选择Java实现。
  • 在输入的args中,匹配字符串,获取不同指令的参数情况。
  • 读取日志文件,通过循环按行获取日志内容,分割匹配相关信息,通过判断字符串,处理增减人数。
  • 需要有一个省份结构存储读取的信息。
  • 根据选项,筛选输出内容。
  • 保存到相应文件中。
      日志文件的内容根据以下情况划分,然后根据划分出来的数组对相应的省份进行操作。

4. 设计实现过程

  根据思路解决相应的功能。

5. 关键代码与思路

  • Province类详细情况
	public static class Province {
		private String name;//省份名称 
		private long ip;//感染患者
		private long sp;//疑似患者
		private long cure;//治愈患者
		private long dead;//死亡患者
		/////////////////初始化相关变量///////////////////
		Province(String name, long ip, long sp, long cure, long dead) {
			this.name = name;
			this.ip = ip;
			this.sp = sp;
			this.cure = cure;
			this.dead = dead;
		}
		////////////////数量变化函数//////////////////////
		public void ipadd(long add) {this.ip += add;}//感染增加
		public void spadd(long add) {this.sp += add;}//疑似增加
		public void cureadd(long add) {this.cure += add;}//治愈增加
		public void deadadd(long add) {this.dead += add;}//死亡增加
		public void ipsub(long sub) {this.ip -= sub;}//感染减少
		public void spsub(long sub) {this.sp -= sub;}//疑似减少
		////////////////获取省份数据的函数////////////////
		public String getname() {return name;}
		public long getip() {return ip;}
		public long getsp() {return sp;}
		public long getcure() {return cure;}
		public long getdead() {return dead;}
	}
  • 对命令行参数进行处理
      在readArgs函数内通过for循环一次对args参数进行判断。单词循环当中用switch对各个参数进行筛选,匹配上的参数就对相应的全局变量进行赋值。如果没有参数的情况,全局变量会保留最初的赋值。
	public static void readArgs(String[] args) {//读取命令行参数
		for (int i = 1; i<args.length; i++) {
			switch (args[i]) {
			case "-log":
				log = args[i+1];
				i++;//跳过log后跟的参数 
				break;//获取日志目录路径
			case "-out":
				outpath = args[i+1];//获取输出路径
				i++;//跳过out后面的参数
				break;
			case "-date"://获取截止日期
				date = args[i+1];
				i++;//跳过date后面跟的参数
				break;
			case "-type"://获取相关输出类型
				for (int j = i+1; j<i+1+4 && j<args.length; j++) {
					switch (args[j]) {
					case "ip":
						type.add("ip"); break;
					case "sp":
						type.add("sp"); break;
					case "cure":
						type.add("cure"); break;
					case "dead":
						type.add("dead"); break;
					}//使用type数组之后记得要初始化
				}
				break;
			case "-province"://获取输出省份
				for (int j = i+1; j<args.length && j<args.length; j++) {
					if (checkprovince(args[j]))
						outputprovince.add(args[j]);
					else break;
				}
				break;
			}
		}
		return;
	}
  • 读取指定目录下的文件
      指定目录下对文件列表的内容按行读取,每行按空格分割根据不同的情况进行匹配,然后存入哈希表相应的省份当中。分割方式如下图:
	public static void getLogFile(String log) {//获取指定目录下的文件
		File logfile = new File(log);
		List<String> fileprovince = new ArrayList<String>();//指定省份涉及的省份
		if (logfile.isDirectory()) {
			String list[] = logfile.list();//获取文件列表
			if (!date.equals("") && !lastDateCheck(list)) {//如果date有参数,且超过最晚日期
				System.out.println("超出最晚日期");
				return ;
			} else {
				for (int i=0; i<list.length; i++) {
					File file = new File(log + "/" +list[i]);//合成目标文件路径
					if (file.isFile() && checkDate(file)) {//判断指定目录的文件下是否为标准文件,且日期是否不大于指定日期
						try {
							FileReader fr = new FileReader(file);
							BufferedReader br = new BufferedReader(fr);
							String str = null;
							while((str = br.readLine())!=null){
//								System.out.println(str);
								String[] arr = str.split(" ");
								if (arr[0].equals("//"))
									break;
								if (outputprovince.size() == 1) {//如果没有outputprovince参数则获取日志中涉及的省作参数
									fileprovince.add(arr[0]);
								}
								if (arr.length == 5) {//湖北 感染患者 流入 福建 2人//湖北 疑似患者 流入 福建 5人
									if (outputprovince.size() == 1) {//如果没有outputprovince参数则获取日志中涉及的省作参数
										fileprovince.add(arr[3]);
									}
									switch (arr[1]) {
									case "感染患者" :
										map.get(arr[3]).ipadd(getLongFromStr(arr[4]));//流入省增加
										map.get(arr[0]).ipsub(getLongFromStr(arr[4]));//流出省减少
										break;
									case "疑似患者" :
										map.get(arr[3]).spadd(getLongFromStr(arr[4]));//流入省增加
										map.get(arr[0]).spsub(getLongFromStr(arr[4]));//流出省减少
										break;
									}
								} else if (arr.length == 4) {//福建 新增 感染患者 2人//福建 新增 疑似患者 5人//湖北 排除 疑似患者 2人//湖北 疑似患者 确诊感染 3人
									switch (arr[1]) {
									case "新增" :
										if (arr[2].equals("感染患者")) {
											map.get(arr[0]).ipadd(getLongFromStr(arr[3]));//感染患者增加
										} else {
											map.get(arr[0]).spadd(getLongFromStr(arr[3]));//疑似患者增加
										}
										break;
									case "排除" :
										map.get(arr[0]).spsub(getLongFromStr(arr[3]));//疑似患者减少
										break;
									case "疑似患者" :
										map.get(arr[0]).spsub(getLongFromStr(arr[3]));//疑似患者减少
										map.get(arr[0]).ipadd(getLongFromStr(arr[3]));//感染患者增加
										break;
									}
								} else if (arr.length == 3) {//湖北 死亡 1人//湖北 治愈 2人		
									switch (arr[1]) {
									case "治愈" :
										map.get(arr[0]).ipsub(getLongFromStr(arr[2]));//感染患者减少
										map.get(arr[0]).cureadd(getLongFromStr(arr[2]));//治愈患者增加
										break;
									case "死亡" :
										map.get(arr[0]).ipsub(getLongFromStr(arr[2]));//感染患者减少
										map.get(arr[0]).deadadd(getLongFromStr(arr[2]));//死亡患者增加
										break;
									}
								}
							}
							br.close();
							} catch (FileNotFoundException e) {
								e.printStackTrace();
							} catch (IOException e) {
								e.printStackTrace();
							}
	                } //else { System.out.println(log + "/" +list[i] + "不为标准文件,或者日期超过最晚日期。"); }
				}
				... ...
				... ...
			}
		} else { System.out.println("输入的log地址不为目录。"); }
	}
  • 结果输出
      全局变量outputprovince在并没有按照拼音排序过,所以在output之前先对需要输出的省份进行排序。然后再根据排完序的provinceout进行指定省份结合type的情况进行输出
	public static void outPut(String[] args) {
//		for (int i = 0; i<outputprovince.size(); i++)
//			System.out.println("output开始:" + outputprovince.get(i));
		String str = new String("");
		List<String> provinceout = new ArrayList<String>();
		int[] index = {
				0,0,0,0,0,
				0,0,0,0,0,
				0,0,0,0,0,
				0,0,0,0,0,
				0,0,0,0,0,
				0,0,0,0,0,
				0,0
		}; 
//		for (int i = 0; i<outputprovince.size(); i++) {
//			System.out.println(outputprovince.get(i));
//		}
		for (int i = 0; i<outputprovince.size(); i++) {
			for (int n = 0; n<provincename.length; n++) {
				if (outputprovince.get(i).equals(provincename[n])) {
					index[n] = 1;
//					System.out.println("n = " + n);
					break;
				}
			}
		}
		for (int i = 0; i<index.length; i++) {
			if (index[i] == 1)
				provinceout.add(provincename[i]);
		}
//		for (int i=0; i<provinceout.size(); i++) {
//			System.out.println("provinceout[" + i + "] = " + provinceout.get(i));
//		}
		if (type.size() == 1) {
			type.add("ip");
			type.add("sp");
			type.add("cure");
			type.add("dead");
		}
		try {
			for (int i = 0; i<provinceout.size(); i++) {
//				System.out.println("1");
			str += map.get(provinceout.get(i)).getname();
//			System.out.println("name");
			for (int n = 0; n<type.size(); n++) {
				switch (type.get(n)) {
			    case "ip":
//			    	System.out.println("ip");
		    		str += " 感染患者" + map.get(provinceout.get(i)).getip() + "人"; break;
		    	case "sp":
//			    	System.out.println("sp");
			    	str += " 疑似患者" + map.get(provinceout.get(i)).getsp() + "人"; break;
			    case "cure":
//			    	System.out.println("cure");
			    	str += " 治愈" + map.get(provinceout.get(i)).getcure() + "人"; break;
		    	case "dead":
//			    	System.out.println("dead");
			    	str += " 死亡" + map.get(provinceout.get(i)).getdead() + "人"; break;
			    }
			}
//			System.out.println(str);
			str += "\r\n";
		    }
		}catch (Exception e) {
			// TODO: handle exception
		}
		str += "// 该文档并非真实数据,仅供测试使用\r\n//命令:";
		for (int i = 0; i<args.length; i++) {
			str += args[i] + " ";
		}
		try {
			FileOutputStream fos = new FileOutputStream(outpath);
			OutputStreamWriter osw = new OutputStreamWriter(fos);
			BufferedWriter bw = new BufferedWriter(osw);
			bw.write(str);
			bw.newLine();
			bw.close();
			osw.close();
			fos.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
		System.out.println(str);
		str = "";
	}

6. 单元测试截图和描述

(1)测试ListOut1.txt

  测试result文件ListOut1.txt中的命令。

(2)测试ListOut2.txt

  测试result文件ListOut2.txt中的命令。

(3)测试ListOut3.txt

  测试result文件ListOut3.txt中的命令。

(4)测试超出最晚日志日期

  要求输入的date不能超出最新的日志日期。

(5)测试log路径出错问题

  考虑log路径出错问题。

(6)测试默认日期、省份、类型

  默认不存在province、type、date选项的输出结果。

(7)测试默认类型的输出顺序

  type默认选项时的输出。

(8)测试指定类型的输出顺序

  type选项指定时的输出。

(9)测试只会列出指定省份

(10)测试不列出全国,只会列出指定省份

  测试不列出全国。

(11)测试边界问题:刚好是最晚日期

  考虑日期边界问题。

7. 单元测试覆盖率优化和性能测试

单元测试覆盖率测试

*  图中的checkprovince用于判断输入参数的省份名称是否存在,其覆盖率与读入的-province带的参数相关。

	public static boolean checkprovince(String str) {
		for (int i = 0; i<provincename.length; i++) {
			if (provincename[i].equals(str))
				return true;
		}
		return false;
	}

8. 代码规范

  我的代码规范->Github仓库:codestyle.md

9. 心路历程与收获

  心路历程:起初看到题目有点长就大概看了一下题目的要求,没有仔细的阅读。但是看完之后就有点懵了,
我已经有段时间没有去写Java代码了,十分生疏再加上本来的底子就不是很厚,我就去在看了一下Java的相关知识点。
但看并没有什么用,最重要的还是要去打代码,多打多练自然而然就熟悉了,就像小学生刚开始学习写字一样,练就了自然就熟悉了,也自然就会了。
开始看到开发的软件要求并不觉得很难,最初的思路还是比较清晰的:读文件,然后再结合参数进行输出。
但是迫于对Java的生疏和最开始没有认真看清楚题目,导致我编程的过程中十分吃力,需要是不是的回头去看题目的要求,再根据实际去修改代码。
这个写了改,改了再写的过程让我非常烦躁。然而烦躁之后还是要继续苦逼的码代码,这再次让我感受到提前规划的重要性,并不能因为项目小而不去提前规划。
在完成开发并进行简单测试符合基本要求之后,我还是能得到心理满足的,毕竟是好不容易得来的成果。
然而这并不代表着本次作业的结束。这次作业需要用到Github,这是我没有接触过的新事物。仔细阅读完相关的使用教程之后,我就自己简单体验了一下。
这体验就给我打开了新世界的大门!之前的我并不知道还有这等好的分享软件。我一直都只是按照课程来学习,也一直苦于找不到相关的学习资源。
找到的CSDN、博客园、菜鸟驿站等等都只是满足基本的入门需求,但是深入的东西并不多,然而这回就让我找到好东西了!
  收获:再一次让我感受到软件工程规划的重要性,也给我敲醒了警钟:编程一定要多练多交流才可以提升自己的水平。

10.技术路线图相关的5个仓库

(1)仓库1:

名称 linux-command
链接 仓库链接
简介   Linux命令大全搜索工具,内容包含Linux命令手册、详解、学习、搜集。

(2)仓库2:

名称 Linux-Tutorial
链接 仓库链接
简介   《Java 程序员眼中的 Linux》/1.Shell常用命令(如启动/停止Web容器,kill进程,查看log等)2.文件管理 3.软件安装 4.在Linux上搭建环境 5.JavaEE项目发布在Linux服务器上,一般来说,Windows环境进行开发,Linux环境进行部署

(3)仓库3:

名称 DD-LinuxDeviceDrivers
链接 仓库链接
简介   内核调试工具集锦, 包括debugfs, trace, gdb, systemtap 的介绍和使用/内核设计的奇技淫巧, 介绍内核中使用的一些高级语法技巧和设计思路

(4)仓库4:

名称 linux-kernel-exploits
链接 仓库链接
简介   linux-kernel-exploits Linux平台提权漏洞集合

(5)仓库5:

名称 Linux-NetSpeed
链接 仓库链接
简介   将Linux现常用的网络加速集成在一起
posted @ 2020-02-20 15:38  绿箭无糖  阅读(112)  评论(0编辑  收藏  举报