天天看點

軟體實踐寒假作業(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
Development 開發 60min
Analysis 需求分析 (包括學習新技術) 180min 240min
Design Spec 生成設計文檔
Design Review 設計複審 600min
Coding Standard 代碼規範 (為目前的開發制定合适的規範) 120min
Design 具體設計
Coding 具體編碼 1200min
Code Review 代碼複審
Test 測試(自我測試,修改代碼,送出修改) 300min
Reporting 報告
Test Repor 測試報告
Size Measurement 計算工作量
Postmortem & Process Improvement Plan 事後總結, 并提出過程改進計劃
合計 3990min

3. 解題思路描述

  由作業要求可知:先讀取日志檔案統計疫情情況,再根據指令行參數輸入的選項要求,輸出相應的文檔。

軟體實踐寒假作業(2/2)
  • 程式語言選擇Java實作。
  • 在輸入的args中,比對字元串,擷取不同指令的參數情況。
  • 讀取日志檔案,通過循環按行擷取日志内容,分割比對相關資訊,通過判斷字元串,處理增減人數。
  • 需要有一個省份結構存儲讀取的資訊。
  • 根據選項,篩選輸出内容。
  • 儲存到相應檔案中。

      日志檔案的内容根據以下情況劃分,然後根據劃分出來的數組對相應的省份進行操作。

軟體實踐寒假作業(2/2)

4. 設計實作過程

  根據思路解決相應的功能。

軟體實踐寒假作業(2/2)

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/2)

(2)測試ListOut2.txt

  測試result檔案ListOut2.txt中的指令。

軟體實踐寒假作業(2/2)

(3)測試ListOut3.txt

  測試result檔案ListOut3.txt中的指令。

軟體實踐寒假作業(2/2)

(4)測試超出最晚日志日期

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

軟體實踐寒假作業(2/2)

(5)測試log路徑出錯問題

  考慮log路徑出錯問題。

軟體實踐寒假作業(2/2)

(6)測試預設日期、省份、類型

  預設不存在province、type、date選項的輸出結果。

軟體實踐寒假作業(2/2)

(7)測試預設類型的輸出順序

  type預設選項時的輸出。

軟體實踐寒假作業(2/2)

(8)測試指定類型的輸出順序

  type選項指定時的輸出。

軟體實踐寒假作業(2/2)

(9)測試隻會列出指定省份

軟體實踐寒假作業(2/2)

(10)測試不列出全國,隻會列出指定省份

  測試不列出全國。

軟體實踐寒假作業(2/2)

(11)測試邊界問題:剛好是最晚日期

  考慮日期邊界問題。

軟體實踐寒假作業(2/2)

7. 單元測試覆寫率優化和性能測試

單元測試覆寫率測試

軟體實踐寒假作業(2/2)

*  圖中的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現常用的網絡加速內建在一起