格式描述git
==========github
課程名稱:軟件工程1916|W(福州大學)正則表達式
做業要求:結對第二次—文獻摘要熱詞統計及進階需求算法
結對學號:221600408_蔡鴻鍵 | 221600409_蔡森林express
做業目標:編程
1、基本需求:實現一個可以對文本文件中的單詞的詞頻進行統計的控制檯程序。windows
2、進階需求:在基本需求實現的基礎上,編碼實現頂會熱詞統計器。app
做業目錄
==========
1.WordCount基本需求
2.思路分析
3.關鍵代碼
4.測試圖片
5.困難與解決
6.心得與總結
7.psp與花絮
做業正文
==========
2.WordCount基本需求
(一)WordCount基本需求
實現一個命令行程序,不妨稱之爲wordCount。
統計文件的字符數:
只須要統計Ascii碼,漢字不需考慮
空格,水平製表符,換行符,均算字符
統計文件的單詞總數,單詞:至少以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。
英文字母: A-Z,a-z
字母數字符號:A-Z, a-z,0-9
分割符:空格,非字母數字符號
例:file123是一個單詞,123file不是一個單詞。file,File和FILE是同一個單詞
統計文件的有效行數:任何包含非空白字符的行,都須要統計。
統計文件中各單詞的出現次數,最終只輸出頻率最高的10個。頻率相同的單詞,優先輸出字典序靠前的單詞。
按照字典序輸出到文件result.txt:例如,windows95,windows98和windows2000同時出現時,則先輸出windows2000
輸出的單詞統一爲小寫格式
輸出的格式爲
characters: number
words: number
lines: number
...
(二)WordCount進階需求
新增功能,並在命令行程序中支持下述命令行參數。說明:字符總數統計、單詞總數統計、有效行統計要求與我的項目相同
從CVPR2018官網爬取今年的論文列表,輸出到result.txt(必定叫這個名字),內容包含論文題目、摘要,格式以下:
爲爬取的論文從0開始編號,編號單獨一行
兩篇論文間以2個空行分隔
在每行開頭插入「Title: 」、「Abstract: 」(英文冒號,後有一個空格)說明接下來的內容是論文題目,或者論文摘要
後續全部字符、單詞、有效行、詞頻統計中,論文編號及其緊跟着的換行符、分隔論文的兩個換行符、「Title: 」、「Abstract: 」(英文冒號,後有一個空格)均不歸入考慮範圍
本部分不參與自動化測試,若有完成,需在博客中詳細描述,並在博客中附件(.exe及.txt)爲證。附加功能的加入不能影響上述基礎功能的測試,分數取決於創意和所展現的完成度,創意沒有天花板,這裏不提出任何限制,盡大家所能去完成。
1 . 解題思路
整體:咱們先抓住需求中的統計函數的關鍵點,再思考分析一些邊緣條件,給出規定,優化函數。
具體:1.WordCount基本需求:從文本文件中逐個讀取全部字符,而後將其保存爲字符串,接着經過正則表達式匹配單詞,保存於TreeMap中,統計並排序,最後將統計結果輸出到文件中。
2.WordCount進階需求:經過Jsoup工具將CVPR2018官網的論文列表爬取下來,保存在result文本文件中,一樣也是進行逐個字符讀取統計,保存字符串進行相應的統計和排序,最後輸出結果。
2 . 設計過程
WordCount的基本需求
- 類圖
- 活動圖
WordCount的進階需求
- 類圖
- 活動圖
3 . 關鍵代碼
WordCount基本需求
//統計字符數 public static void countChars()throws Exception{ countChars = 0; File file = new File(path); BufferedReader br = new BufferedReader(new FileReader(file)); StringBuffer sbf = new StringBuffer(); int val; while((val=br.read())!=-1){ if((0<=val && val<=9)){ countChars++; }else if(val == 10){ continue; }else if(11<=val && val<=127){ countChars++; } sbf.append((char)val); } br.close(); fileContent = sbf.toString().toLowerCase(); }
//統計有效行數 public static void countLines()throws Exception{ countLines = 0; String regex = "\\s*"; File file = new File(path); BufferedReader br = new BufferedReader(new FileReader(file)); String line; while((line=br.readLine())!=null){ if(!line.matches(regex)){ countLines++; } } br.close(); }
//統計單詞數 public static void countWords(){ countWords = 0; Pattern expression = Pattern.compile("[a-z]{4,}[a-z0-9]*"); String str = fileContent; Matcher matcher = expression.matcher(str); String word; while (matcher.find()) { word = matcher.group(); countWords++; if (records.containsKey(word)) { records.put(word, records.get(word) + 1); } else { records.put(word, 1); } } }
//統計熱門單詞個數 public static void countHotWords(){ sortWord(); String str; int length = wordList.size(); if(length > 10){ for(int i = 0; i < 10; i++){ str = wordList.get(i); hotWordList.add(str); } } else{ hotWordList.addAll(wordList); } }
//對統計完的單詞進行排序 public static void sortWord(){ List<Map.Entry<String, Integer>> list = new ArrayList<>(records.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return o2.getValue().compareTo(o1.getValue()); } }); String str; for (Map.Entry<String, Integer> e: list) { str = "<"+e.getKey()+">: "+e.getValue(); wordList.add(str); } }
WordCount進階需求
//線程爬取官網連接 public static void crawler(){ try{ String url = "http://openaccess.thecvf.com/CVPR2018.py"; crawlerLink(url); new Thread(new Runnable() { @Override public void run() { while(!links.isEmpty()){ String link = links.getFirst(); links.removeFirst(); crawlerContent(link); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); }catch (Exception e){ e.printStackTrace(); } }
//爬取全部文章連接 public static void crawlerLink(String url){ try{ Document doc = Jsoup.connect(url).get(); Element content = doc.getElementById("content"); Elements ptitles = content.getElementsByClass("ptitle"); for(Element item : ptitles){ Element link = item.selectFirst("a"); String href = "http://openaccess.thecvf.com/"+link.attr("href"); links.add(href); } }catch (Exception e){ e.printStackTrace(); } }
//獲取全部文章的標題和摘要 public static void crawlerContent(String url){ try{ StringBuilder results = new StringBuilder(); Document doc = Jsoup.connect(url).get(); Element content = doc.getElementById("content"); results.append(index+"\r\n"); String title = content.getElementById("papertitle").text(); results.append("title: "+title+"\r\n"); String abstractText = content.getElementById("abstract").text(); results.append("abstract: "+abstractText+"\r\n"+"\r\n"+"\r\n"); saveArticle(results); index++; }catch (Exception e){ e.printStackTrace(); } }
//將爬取的內容保存到result文本 public static void saveArticle(StringBuilder content)throws Exception{ File file = new File(path+"result.txt"); if(!file.exists()){ file.createNewFile(); } FileWriter fw = new FileWriter(file,true); BufferedWriter bw = new BufferedWriter(fw); bw.write(content.toString()); bw.flush(); bw.close(); }
if(args.length == 0){ throw new IllegalArgumentException(); } for (int i = 0; i < args.length; i++) { if(args[i].equals("-i")){ orders.put("-i",args[i+1]); i++; }else if(args[i].equals("-o")){ orders.put("-o",args[i+1]); i++; }else if(args[i].equals("-w")){ orders.put("-w",args[i+1]); i++; }else if(args[i].equals("-m")){ orders.put("-m",args[i+1]); i++; }else if(args[i].equals("-n")){ orders.put("-n",args[i+1]); i++; } }
//命令行參數賦值 public static void terminal(){ inputPath = path + orders.get("-i"); outputPath = path + orders.get("-o"); w = Integer.valueOf(orders.get("-w")); if(orders.containsKey("-m")){ m = Integer.valueOf(orders.get("-m")); isGroup = true; } if(orders.containsKey("-n")){ n = Integer.valueOf(orders.get("-n")); }else{ n = 10; } }
//讀取輸入文件全部字符內容 public static void readFile()throws Exception{ File file = new File(inputPath); BufferedReader br = new BufferedReader(new FileReader(file)); int val; while((val=br.read())!=-1){//統計全部字符數 if((0<=val && val<=9)){ countAllChars++; }else if(val == 10){ continue; }else if(11<=val && val<=127){ countAllChars++; } } br.close(); } //獲取文章標題,摘要字符內容及有效行數 public static void dealFile()throws Exception{ String regex = "\\d+"; File file = new File(inputPath); if(!file.exists()){ file.createNewFile(); } BufferedReader br = new BufferedReader(new FileReader(file)); String line,title,abstractText; while((line=br.readLine())!=null){ if(line.matches(regex)){ countNums += line.length() + 1;//統計論文編號所在行字符數 } else if(line.startsWith("title: ")){ countLines++;//統計有效行數 countNums += 7;//統計title: 字符數 title = line.substring(6).toLowerCase(); if(w == 0){ countWords(title,1); }else if(w == 1){ countWords(title,10); } if(isGroup){ if(w == 0){ countWordGroups(title,1); }else if(w == 1){ countWordGroups(title,10); } } }else if(line.startsWith("abstract: ")){ countLines++;//統計有效行數 countNums += 10;//統計abstract: 字符數 abstractText = line.substring(9).toLowerCase(); countWords(abstractText,1); if(isGroup){ countWordGroups(abstractText,1); } } } br.close(); countHotWords(); if(isGroup){ countHotWordGroups(); } }
//統計單詞 public static void countWords(String content, int weight){//weight權重的詞頻統計 Pattern expression = Pattern.compile("[a-z]{4,}[a-z0-9]*");//匹配單詞 String str = content; Matcher matcher = expression.matcher(str); String word; while (matcher.find()) { word = matcher.group(); countWords++; if (records.containsKey(word)) { records.put(word, records.get(word) + weight); } else { records.put(word, weight); } } }
//統計單詞詞組 public static void countWordGroups(String content, int weight){//詞組權重詞頻統計 StringBuffer sbf = new StringBuffer(); int size = content.length(); char []arr = content.toCharArray(); for (int i = 0; i < size; i++) {//去除分隔符整合全部字符 char val = arr[i]; if(48<=val && val<=57){ sbf.append(val); }else if(97<=val && val<=122){ sbf.append(val); } } allText = sbf.toString();//獲取全部字符(去分隔符後) Pattern expression = Pattern.compile("[a-z]{4,}[a-z0-9]*"); String str = content; Matcher matcher = expression.matcher(str); String word; ArrayList<String> group = new ArrayList<>(); while (matcher.find()) {//提取單詞 word = matcher.group(); group.add(word);//單詞歸組 } int len = group.size(); for (int i = 0; i <= len-m; i++) { String pr = ""; String pr2 = ""; for (int j = i; j < i+m; j++) {//將m個單詞構成字符串pr,pr2爲記錄單詞構成的詞組 pr += group.get(j); pr2 += group.get(j); if(j < (i+m)-1){ pr2 +=" "; } } if(allText.indexOf(pr)!=-1){//在allText中匹配子字符串pr if (records2.containsKey(pr2)) {//詞組歸組 records2.put(pr2, records2.get(pr2) + weight); } else { records2.put(pr2, weight); } } } }
//統計熱度單詞 public static void countHotWords(){ sortWords(); String str; int length = wordList.size(); if(length > n){ for(int i = 0; i < n; i++){ str = wordList.get(i); hotWordList.add(str); } } else{ hotWordList.addAll(wordList); } }
//統計熱度詞組 public static void countHotWordGroups(){ sortWordGroups(); String str; int length = wordGroupList.size(); if(length > n){ for(int i = 0; i < n; i++){ str = wordGroupList.get(i); hotWordGroupList.add(str); } } else{ hotWordGroupList.addAll(wordGroupList); } }
//單詞排序 public static void sortWords(){ List<Map.Entry<String, Integer>> list = new ArrayList<>(records.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return o2.getValue().compareTo(o1.getValue()); } }); String str; for (Map.Entry<String, Integer> e: list) { str = e.getKey()+":"+e.getValue(); wordList.add(str); } }
//詞組排序 public static void sortWordGroups(){ List<Map.Entry<String, Integer>> list = new ArrayList<>(records2.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return o2.getValue().compareTo(o1.getValue()); } }); String str; for (Map.Entry<String, Integer> e: list) { str = "<"+e.getKey()+">:"+e.getValue(); wordGroupList.add(str); } }
4.WordCount基本需求單元測試
- 樣例1
- 測試1結果
- 樣例2
- 測試2結果
- 樣例3
- 測試3結果
5.交流與討論
D:每一個人分工不均
A:多商量多討論
D:需求理解不到位
A:分析理解討論需求
D:代碼BUG改不出來
A:多多時候搜索引擎
6.困難與解決
7.心得與總結
最大的收穫,應該就是發現問題,解決問題的過程,有時候由於一小小的細節,弄得本身焦頭爛額,可是當忽然找到問題的所在時,內心頓時輕鬆了不少。這或許是調試bug必然的過程。此外,咱們也學到了一些實用的技能,如Jsoup工具的使用,GitHub的項目的上傳等。
此次結對我對於團隊的做用比較小,主要負責文檔的編寫與GITHUB的上傳和代碼測試優化,在代碼這一塊涉及比較小,主要是由於分工不明。我認爲之後團隊編碼時應該你們先聚在一塊兒討論需求題目,相互信任,這纔會使團隊做用最大化。
隊友此次主要負責代碼部分,特別認真,考慮周到,分析需求到位。
此次結對編程,整體來講,收穫仍是挺大的,我主要負責代碼編程,不少時候常常被一小bug弄得焦頭爛額,可是有時候忽然靈光一現,那又是多麼使人開心的事。這次的編程,不只讓個人編程能力獲得了鍛鍊,也讓我學到了一些新的知識,更重要的是,團隊協做,不只能夠督促本身,並且更能提升效率。
隊友此次主要負責項目上傳,博客撰寫以及後期代碼測試和優化,時不時給我提供思路參考。
此次任務涉及代碼部分但實際上是模仿如今公司的代碼編程規範,如今讓咱們接觸這些規範是有好處的,能及時的改爲咱們的錯誤的編程,培養良好的,高效率的編程。這是很是重要的。
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分 鍾) |
Planning | 計劃 | ||
? Estimate | ? 估計這個任務須要多少時間 | 600 | 550 |
Development | 開發 | ||
? Analysis | ? 需求分析 (包括學習新技術) | 30 | 60 |
? Design Spec | ? ?成設計?檔 | 20 | 30 |
? Design Review | ? 設計複審 | 20 | 10 |
? Coding Standard | ? 代碼規範 (爲目前的開發制定合適的規範) | 20 | 20 |
? Design | ? 具體設計 | 120 | 250 |
? Coding | ? 具體編碼 | 360* | 480* |
? Code Review | ? 代碼複審 | 30 | 30 |
? Test | ? 測試(自我測試,修改代碼,提交修改) | 30 | 20 |
Reporting | 報告 | ||
? Test Repor | ? 測試報告 | 10 | 15 |
? Size Measurement | ? 計算工做量 | 15 | 10 |
? Postmortem & Process Improvement Plan | ? 過後總結, 並提出過程改進計劃 | 30 | 25 |
合計 | 685 | 950 |