課程連接 | 軟件工程1916|W(福州大學) |
做業要求 | 結對第二次—文獻摘要熱詞統計及進階需求 |
結對學號 | 221600207|221600205 |
做業目標 | 本次做業分爲兩部分: 1、基本需求:實現一個可以對文本文件中的單詞的詞頻進行統計的控制檯程序。 2、進階需求:在基本需求實現的基礎上,編碼實現頂會熱詞統計器 |
做業正文 | 做業正文 |
基礎需求 | WordCount |
進階需求 | WordCountPro |
提交截圖以下
html
本此做業中進階需求裏的-m功能還沒有完成,基本思路爲將讀取的String分割爲單詞,建立數組並依次記錄單詞出現的下標,非法單詞記錄下標爲-1。即String="key,hello word"對應數組爲a[0]=0;a[1]=-1;a[2]=4;a[3]=10;取m=2時則取出subString(a[2],a[3]+a[3].length);java
221600207黃權煥git
221600205陳紅寶github
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
• Estimate | • 估計這個任務須要多少時間 | 900 | |
Development | 開發 | ||
• Analysis | • 需求分析 (包括學習新技術) | 120 | 600 |
• Design Spec | • 生成設計文檔 | 60 | 60 |
• Design Review | • 設計複審 | 30 | 60 |
• Coding Standard | • 代碼規範 (爲目前的開發制定合適的規範) | 30 | 30 |
• Design | • 具體設計 | 120 | 200 |
• Coding | • 具體編碼 | 600 | 900 |
• Code Review | • 代碼複審 | ||
• Test | • 測試(自我測試,修改代碼,提交修改) | 30 | 120 |
Reporting | 報告 | 60 | 150 |
• Test Repor | • 測試報告 | 30 | 30 |
• Size Measurement | • 計算工做量 | 30 | 30 |
•Postmortem & Process Improvement Plan | • 過後總結, 並提出過程改進計劃 | 30 | 30 |
合計 | 1110 | 2210 |
獲取行數
將文件打開後,用readLine()函數逐行讀取文本內容並保存在fContent上,此時疊加行數。算法
獲取字符數
將字符串fContent讀取成功後,字符數+=fContent.length;編程
獲取單詞數
將fContent使用split("\W+")分割成只有可寫字符的單詞組存入String [] ch中,單詞數+=ch.length;數組
數據結構
使用HashMap保存單詞和使用頻率,不使用TreeMap的緣由是,TreeMap沒有自帶按值排序後,相同值按字典序排序的特性。而HashMap可使存儲、查找的時間效率都在O(1)內完成,而不是TreeMap的log(N);數據結構
詞頻排序
值得注意的是,Map自己排序須要轉化成List,排序成功後應將結果應從新轉化爲LinkedHashMap。
LinkedHashMap能夠按插入順序保存,方便後續使用。時間複雜度N*log(N);函數
自定義輸入輸出
在類中額外保存輸入輸出名便可。性能
自定義詞頻統計輸出
在類中額外保存一個最大單詞數用來控制LinkedHashMap長度便可。
權重分析
在HashMap插值時,額外判斷是否來自Title,是的話記錄數+10,不然+1便可。
詞組詞頻統計功能
詞組詞頻統計分析因爲時間關係還沒有實現,思路在寫在前面已介紹過。將讀取的String分割爲單詞,建立數組並依次記錄單詞出現的下標,非法單詞記錄下標爲-1。即String="key,hello word"對應數組爲a[0]=0;a[1]=-1;a[2]=4;a[3]=10;取m=2時則取出subString(a[2],a[3]+a[3].length);取m=1時則取出subString(a[2],a[2]+a[2].length)等。
多參數的混合使用
讀取一行,依舊用split("-")函數分割成不一樣指令,分別調用函數便可。
諸位仍是到我隊友文章查看,因爲本人沒法總結,且他文章字數太多,我再也不贅述。221600205
lin.java完成函數的封裝。其中setWord接受文件名,調用setMap,調用isLow;getWord輸出文件,調用sortMap。 Main.java進行調用和設置lib類的參數。
類圖以下
存放時的流程圖以下
單元測試圖
單元測試代碼
import static org.junit.Assert.*; import org.junit.Test; public class libTest { @Test public void testSetFileInput() { lib b = new lib(); b.setFileInput("input.txt"); assertEquals("input.txt",b.getFileInput()); } @Test public void testSetFileOutput() { lib b = new lib(); b.setFileOutput("output.txt"); assertEquals("output.txt",b.getFileOutput()); } @Test public void testSetWValue() { lib b = new lib(); b.setWValue(3); assertEquals(true,b.getWValue()); } @Test public void testSetMValue() { lib b = new lib(); b.setMValue(5); assertEquals(5,b.getMValue()); } @Test public void testSetMaxWordNum() { lib b = new lib(); b.setMaxWordNum(5); assertEquals(5,b.getMaxWordNum()); } @Test public void testGetFWordCount() { lib b = new lib("input.txt"); b.setWord(); assertEquals(9,b.getFWordCount()); } @Test public void testGetFRowCount() { lib b = new lib("input.txt"); b.setWord(); assertEquals(2,b.getFRowCount()); } @Test public void testGetfByteCount() { lib b = new lib("input.txt"); b.setWord(); b.getWord(); assertEquals(74,b.getfByteCount()); } @Test public void testGetMaxWordNum() { lib b = new lib("input.txt"); b.setMaxWordNum(12); assertEquals(12,b.getMaxWordNum()); } @Test public void testIsLower() { lib b = new lib(); assertEquals(false,b.isLower('A')); } @Test public void testIsDigit() { lib b = new lib(); assertEquals(true,b.isDigit('0')); } }
public class lib { private int fWordCount = 0;//字詞數 private int fRowCount = 0;//行數 private int fByteCount = 0;//字節數 private int maxWordNum = 10;//詞頻輸出數 private String fileInput= "cvpr/result.txt"; private String fileOutput= "src/output.txt"; private boolean wValue = false;//-w 值 private int mValue = 1;//-m 值 private HashMap<String,Integer> map = new HashMap<String,Integer>();//存放字詞處 //…… }
public void setWord() { try { String fContent = ""; FileInputStream fis = new FileInputStream(fileInput); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); fWordCount = fByteCount = fRowCount = 0; while ((fContent = br.readLine()) != null) { if(fContent.length() > 3)//排除空行和編號 { fRowCount ++; if(fContent.charAt(0) == 'T') { fContent = fContent.substring(7, fContent.length()-1 );//remove(Title:) //換行+1 fByteCount += fContent.length()+1; if(mValue >= 1 ) setMap(fContent,true); else setMapPro(fContent,true); } else if(fContent.charAt(0) == 'A') { fContent = fContent.substring(9, fContent.length()-1 );//remove(Abstract: ) //換行+1 fByteCount += fContent.length()+1; if(mValue >= 1 ) setMap(fContent,false); else setMapPro(fContent,false); } } } fis.close(); } catch (FileNotFoundException e) { System.out.print("文件不存在"); e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
public void setMap(String fContent,boolean isTitle) { String [] ch = fContent.split("\\W+"); for(int i = 0; i< ch.length ;i++) { if(ch[i].length()>=4) { ch[i] = ch[i].toLowerCase(); if (isLower(ch[i].charAt(0)) && isLower(ch[i].charAt(1)) && isLower(ch[i].charAt(2)) && isLower(ch[i].charAt(3)) ) { //System.out.print(ch[i]); //新增紀錄或者記錄數+1 fWordCount ++; if( map.containsKey(ch[i]) ) map.put(ch[i],(wValue & isTitle) ? map.get(ch[i])+10 : map.get(ch[i])+1); else map.put(ch[i], (wValue & isTitle) ? 10 : 1); } } } }
public LinkedHashMap<String,Integer> sortMap(int num) { List<Map.Entry<String,Integer>> list = new ArrayList<Map.Entry<String,Integer>>(map.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return o2.getValue() != o1.getValue() ? (o2.getValue() - o1.getValue()) : (o1.getKey()).toString().compareTo(o2.getKey()); //return (o1.getKey()).toString().compareTo(o2.getKey()); } }); LinkedHashMap<String,Integer> tmp = new LinkedHashMap<String,Integer>(); for (int i = 0; i < list.size() && i< num; i++) { String id = list.get(i).toString(); Integer value = list.get(i).getValue(); tmp.put(id, value); //System.out.println(id + (value)); } return tmp; }
public void getWord() { LinkedHashMap<String,Integer> list = sortMap(maxWordNum); try { FileOutputStream fos = new FileOutputStream(fileOutput); OutputStreamWriter osw = new OutputStreamWriter(fos); BufferedWriter buff = new BufferedWriter(osw); String content = "characters: " + fByteCount + "\r\n"; content += "words: "+ getFWordCount() + "\r\n"; content += "lines: "+ fRowCount + "\r\n"; Iterator<String> iterator = list.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next(); content += "<" + key.replace("=", ">: ") + "\r\n"; //System.out.println(key.replace("=", ">: ")); } //System.out.print(content); buff.write(content); buff.flush(); fos.close(); } catch (FileNotFoundException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } }
在sortMap排序中,我使用的是java自帶的sort函數,故時間效率爲Nlog(N).可是咱們也能夠改成維護一個最大堆MaxHeap,設定堆的容量爲maxWordNum,如下簡稱M。則時間效率能夠改進爲Nlog(M),在一般狀況下,M很小,log(M)約等於1,惋惜java沒有現成的最大堆。
本次任務時間相對緊張,像性能測試和單元測試並無作得很好。此次結對暴露出一些問題,我是想好的時間計劃很容易被隊友的編程速度戰勝。好比此次,我獨立完成了基礎需求和進階需求的大部分代碼,即使如此,我依舊是在做業發佈的第五天才收到我隊友的爬蟲相關代碼。
另外不能否認的是,項目進度拖延也有個人一部分責任,儘管我是近乎天天都有跟進與催促,但效果甚微,我卻在完善和測試本身的代碼,沒有過多給與支持(可能我是不想一我的完成整個項目吧,攤手.jpg)
對於此次項目的要求,GitHub、單元測試與jProfiler的使用也花費了我很多時間,且因爲最初測試時沒有使用單元測試,函數也不夠細緻,致使後續的單元測試只能很是簡單,性能分析也草草結束。(這三項幾乎花了我一半的代碼時間,不能小覷)。
關於隊友,我仍是承認他的學習態度的,他也因熬夜寫代碼而熬到了四點餘。儘管我接觸的爬蟲是依據文檔樹查找葉節點實現的,而他是經過匹配正則而實現,但我並不清楚他這麼作到底有多難,也就無從評價,仍是請諸君移步至他的博客一觀。
在文章末尾,我也想借此提出疑問:一個軟件項目中,你沒法準確評估隊友的編程能力高低,那麼是事前分工和計劃被打破的狀況下,如何從新組織分工與計劃呢?