結對第二次—文獻摘要熱詞統計及進階需求

班級:軟件工程1916|W(福州大學)
做業:結對第二次—文獻摘要熱詞統計及進階需求
結對學號:221600315 黎煥明 221600319 李彥文
GitHub基礎需求項目地址:基礎需求
GitHub進階需求項目地址:進階需求
做業目標: 實現一個可以對文本文件中的單詞的詞頻進行統計的控制檯程序。在基本需求實現的基礎上,編碼實現頂會熱詞統計器。
具體分工:兩我的一塊兒負責分析需求,而後主要分析需求和文檔編寫主要是221600319,221600315主要負責部分代碼的實現。git

1、github代碼簽入記錄

  • 基本需求
  • 進階需求

2、PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃
• Estimate • 估計這個任務須要多少時間 660 1010
Development 開發
• Analysis • 需求分析 (包括學習新技術) 180 60
• Design Spec • 生成設計文檔 60 180
• Design Review • 設計複審 60 120
• Coding Standard • 代碼規範 (爲目前的開發制定合適的規範)
• Design • 具體設計 120 180
• Coding • 具體編碼 240 300
• Code Review • 代碼複審 0 200
• Test • 測試(自我測試,修改代碼,提交修改) 60 90
Reporting 報告
• Test Report • 測試報告
• Size Measurement • 計算工做量
• Postmortem & Process Improvement Plan • 過後總結並提出過程改進計劃 60 60
合計 660 1010

3、解題思路

1. 基本需求:拿到題目後,看到基本需求時感受並不難,由於以前寫過相似的程序。要求知足三個功能,第一個是字符統計,第二個是單詞統計,第三個是詞頻統計。基本想法是將這三個功能分別寫到三個函數中,而後經過主函數調用返回相應的結果而且打印輸出。
2. 進階需求:是在基本需求的基礎上添加了參數判斷要求,新增的功能是加入詞組統計、單詞或詞組加入權重來計算最終的頻數。參數判斷單獨封裝,程序執行開始前就要將全部參數所有處理,方便以後使用,因此將參數判斷結果利用類屬性記錄。首先是加入權重這個問題,咱們的想法是直接在詞頻統計函數中加入判斷功能,若是開啓權重功能,那麼屬於TITLE的詞出現一次詞頻加10,其餘狀況都同樣。詞組統計功能,將這個功能單獨分裝成一個函數,利用正則來匹配詞組。
3. 資料查找:通過初步分析以後,上網看了一些介紹單詞統計,詞組統計的博客。大體瞭解了一下他們的思路,基本有兩種,一種是循環讀取判斷,再一種就是正則表達式匹配。咱們更傾向於後一種,這種方法比較直觀,體現再代碼上就是比較簡潔。

4、基本需求設計實現過程:

  • 代碼如何組織:
一共用兩個類,一個類是主程序入口Main,另一個是文本處理類Lib。Main類調用Lib類的靜態函數countChars、countWords、countLines分別統計字符數單詞數和行數,而後直接輸出,總體結構較爲簡單。
  • 單元測試:

  • 類圖:
  • 程序改進思路:
在基本功能完成後,進行測試時發現,若是測試文件比較大,程序讀取文件很是緩慢。經過debug發現是由於在讀取文件時使用了節點流,當文件讀取採用處理流方式時文件讀取會快不少。
由於剛開始寫時需求分析時間不多就開始寫代碼,致使咱們直接用了比較「土」的方法來實現,在進階需求寫爬蟲纔想起來用正則表達式實現能夠簡單地很是多。同時由於List<StringBuilder>的deleteChar方法效率很是低,咱們直接從新分配了內存,這部分處理時間複雜度縮短了很是多。
  • 代碼說明及流程圖:
基礎需求實現比較簡單,可是countWords有必定的難度,剛開始並無想到用正則,而是直接遍歷。該方法接收StringBuilder的List並返回一個哈希表,該方法遍歷List的每一行,而後遍歷該行,若是找到單詞就檢查哈希表是否存在,若是哈希表存在該單詞就取出哈希表裏面的值+1並put進哈希表,若是哈希表不存在該單詞,則將該單詞直接put進哈希表,key爲單詞,value爲1。最後對哈希表進行按值排序並返回哈希表。
/**
     * 統計單詞數
     * @param stringList
     * @return
     */
    public static Map<String,Integer> countWords(List<StringBuilder> stringList){
        Map<String,Integer> hashTable=new LinkedHashMap<>();
        /* 計數器 */
        int count;
        /* 是否爲單詞的標誌*/
        boolean flag;

        /* 統計單詞數,若是存在單詞就放進哈希表 */
        for(StringBuilder line:stringList){
            count=0;
            flag=true;
            for(int i=0;i<line.length();++i){
                if(line.charAt(i)<='z'&&line.charAt(i)>='a'){
                    ++count;
                }else if(line.charAt(i)<='9'&&line.charAt(i)>='0'){
                    if(count<4){
                        flag=false;
                    }
                    ++count;
                }else if(count>3&&flag){
                    String key=line.substring(i-count,i);

                    /* 將單詞存入哈希表 */
                    hashTable.merge(key, 1, (a, b) -> a + b);
                    count=0;
                }else{
                    count=0;
                    flag=true;
                }
            }
        }

        /* 對哈希表進行排序 */
        List<Map.Entry<String, Integer>> keyList = new LinkedList<Map.Entry<String, Integer>>(hashTable.entrySet());
        keyList.sort( new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1,
                               Map.Entry<String, Integer> o2) {
                return Integer.compare(o2.getValue().compareTo(o1.getValue()), 0);
            }

        });
        hashTable.clear();
        for(Map.Entry<String, Integer> entry:keyList){
            hashTable.put(entry.getKey(),entry.getValue());
        }

        return hashTable;
    }
  • 流程圖:

5、進階需求設計實現過程

  • 代碼如何組織 :
一樣是兩個類,一個類是主程序入口Main,另一個是文本處理類Lib。Main類調用Lib類的靜態函數getParameter得到參數,而後根據參數調用readFile讀取文件,以及打開輸出文件流,調用Lib類靜態函數getContent得到result.txt中的內容,而後Main類調用Lib類的靜態函數countChars、countWords、countLines分別統計字符數單詞數和行數,而後直接輸出。
  • 單元測試:

  • 類圖
    github

  • 代碼說明及流程圖:
進階需求總體比較難,主要難在詞組詞頻統計,將獲取到的文本放在兩個List<StringBuilder>中,行的一行字符串並記錄爲volatileString,使用正則表達式「[^a-z0-9]」+對volatileString進行分割,獲得一個數組的單詞,而後對數組內全部單詞使用"^[a-z]{4}[a-z0-9]*" m個一組進行正則匹配,肯定該單詞是否爲單詞,若是爲詞組,經過使用正則表達式「單詞1[^a-z0-9]+單詞2[^a-z0-9]+單詞m」對volatileString進行匹配獲得包含分隔符的完整單詞str(①),而後將str同基礎需求同樣存入map,而後經過volatileString.indexof(單詞1)和單詞1.length獲得單詞1初始位置beginindex和結束位置endIndex,而後將line賦值爲截取0-beginIndex和endIndex到volatileString.length(),以上操做是爲了不①位置匹配到不相符的結果。
/**
     * 統計詞組詞頻
     * @param stringList
     * @return
     */
    static Map<String,Integer> countWords(int delete, int m, List<StringBuilder> stringList){
        Map<String,Integer> hashTable=new LinkedHashMap<>();

        // 建立 Pattern 對象
        String patternString = "^[a-z]{4}[a-z0-9]*";
        Pattern pattern = Pattern.compile(patternString);

        boolean flag;
        for(StringBuilder line:stringList){
            String volatileString= "";
            if(line.length()>delete) {
                volatileString = line.substring(delete);
            }
            String notAlphaandNumber="[^a-z0-9]+";
            String [] strings=volatileString.split(notAlphaandNumber);
            for(int i=0;i<strings.length-m+1;i++){

                //是否全爲單詞
                flag=true;
                for(int j=i;j<i+m&&j<strings.length;++j){
                    Matcher matcher = pattern.matcher(strings[j]);
                    if(!matcher.find()){
                        flag=false;
                        break;
                    }
                }
                //若是全爲單詞
                if(flag){
                    StringBuilder regEx= new StringBuilder();
                    for(int k=0;k<m;++k){
                       regEx.append(strings[i + k]);
                       if(k!=m-1)regEx.append(notAlphaandNumber);
                    }
                    Pattern compile = Pattern.compile(regEx.toString());
                    Matcher matcher=compile.matcher(volatileString);
                    if(matcher.find()){
                        hashTable.merge(matcher.group(), 1, (a, b) -> a + b);
                    }
                }
                int beginIndex=volatileString.indexOf(strings[i]);
//                int endIndex=volatileString.indexOf(strings[i+m-1])+strings[i+m-1].length();
                int endIndex=volatileString.indexOf(strings[i])+strings[i].length();
//                volatileString=volatileString.substring(0,beginIndex)+volatileString.substring(endIndex);
                volatileString=volatileString.substring(0,beginIndex)+volatileString.substring(endIndex);
            }
        }
        return hashTable;
    }

  • 改進的思路:
剛開始也是使用基本需求那樣一個個比對,可是由於寫爬蟲的時候忽然想起來還有正則表達式,因而直接使用正則表達式進行匹配,而後使用Pattern.compile能夠更加快速的進行正則匹配,同時由於String是Immutable的,因此進階需求和基礎需求同樣都是使用StringBuilder進行字符串處理。
  • 項目測試
    • 基本需求


    • 進階需求

6、爬蟲簡介

爬蟲是本身使用Python實現的,直接用正則匹配全部div爲ptitle的節點並獲取連接,而後對連接的網頁內容進行正則匹配將結果輸出到文件中。
使用方法:Python main.py
# coding=utf-8
import re
import requests
respose=requests.get('http://openaccess.thecvf.com/CVPR2018.py')
text=respose.text 
urls=re.findall(r'class="ptitle".*?href="(.*?)"',text,re.S)  
output=open("result.txt","w",encoding='utf-8')
j=0
print(respose.encoding)
for i in urls:
    url='http://openaccess.thecvf.com/'+i
    respose = requests.get(url)
    text = respose.text
    paper_title = re.findall('id="papertitle">\n(.*?)<',text, re.S)
    abstract = re.findall('id="abstract" >\n(.*?)<', text, re.S) 
    output.write(str(j)+"\n")
    output.write("Title: ")
    output.write(paper_title[0])
    output.write("\n")
    output.write("Abstract: ")
    output.write(abstract[0])
    output.write('\n\n\n')
    j+=1

7、遇到的困難與解決方法

  • 需求分析不夠
重頭開始需求分析,而後重構代碼。
  • 代碼耦合性比較高
經過分離不一樣的模塊和功能下降總體的耦合度。
  • 兩我的溝通不夠及時形成了理解分歧
下次記得及時和多溝通。

8、隊友評價

221600319在本次做業中有點被我瞎帶節奏的感受,他的軟件工程思惟比較好,可以積極認真的分析項目的需求,可是本次由於我我的的緣由致使咱們此次做業提交比較遲,但願我本身之後能夠不要這麼瞎帶節奏了吧!而後但願之後可以一塊兒使用軟件工程的方法高效、好好的完成做業!
相關文章
相關標籤/搜索