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

課程連接:軟件工程1916|W(福州大學)
做業要求:結對第二次—文獻摘要熱詞統計及進階需求
結對學號:221600205 | 221600207
做業目標1:1、基本需求:實現一個可以對文本文件中的單詞的詞頻進行統計的控制檯程序。
做業目標2:2、進階需求:在基本需求實現的基礎上,編碼實現頂會熱詞統計器html

團隊分工:

黃權煥:
1.主要代碼實現
2.需求分析討論
3.博客撰寫
4.代碼測試git

陳紅寶:
1.爬蟲代碼實現
2.需求分析討論
3.博客撰寫
4.代碼測試github

做業正文

1、PSP表格

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

2、解題思路描述

說太多的解題思路描述,反倒像是過後諸葛亮。我就着實的描述一下當時的狀況。

記得開始工做的時候是上週六(3月9號)。週五晚上發佈做業,大概看了一下需求。從感受上來看,有些多,也有些複雜。
因而,週六早上和下午完成其餘工做後,就開始討論本次做業了。
可是,問題發生了!個人Eclipse用不了,要從新配置環境變量。跟着百度上的教程,一步一步的走,但老是解決一個錯誤又出現另一個錯誤。期間,環境變量的配置方式都用了三種。花費一個晚上的時間,最後全部東西都卸載重裝,問題終於解決了!頓時鬆了一口氣。
但問題是,個人同伴,在這個晚上,已經把基礎需求代碼所有寫出來了。
就像那句話怎麼說:時間是講公平的,當你忙於一件事情的時候,有人已經把另一件事情忙完了!
有些慚愧,由於沒有太多的討論,以及對他有任何幫助!他一我的默默的完成了!
以後的一天也就是上週日(3月10號)晚上。我作的事情是理解同伴編寫的代碼。同時也理解做業的需求。
有不少收穫,也發現了一些問題,以後會一一道來。正則表達式

3、設計實現過程

基礎需求實現

(1)需求分析

第一步、實現基本功能

1.統計文件的字符數:

- 只須要統計Ascii碼,漢字不需考慮

- 空格,水平製表符,換行符,均算字符

2.統計文件的單詞總數,單詞:至少以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。

英文字母: A-Z,a-z

字母數字符號:A-Z, a-z,0-9

分割符:空格,非字母數字符號

例:file123是一個單詞,123file不是一個單詞。file,File和FILE是同一個單詞

3.統計文件的有效行數:任何包含非空白字符的行,都須要統計。

4.統計文件中各單詞的出現次數,最終只輸出頻率最高的10個。頻率相同的單詞,優先輸出字典序靠前的單詞。

5.按照字典序輸出到文件result.txt:例如,windows95,windows98和windows2000同時出現時,則先輸出windows2000

輸出的單詞統一爲小寫格式

分析:從宏觀上來講,就是輸入一個文件的內容,而後截取以4個英文字母開頭及以上的單詞,輸出。並統計單詞總數,有效行數,各單詞的出現次數。

而後,細分下來就是實現了!

實現講解:

1.從文件中讀取字符流到緩衝區,判斷緩衝區的每一行字符,切割字符串(去掉非字母非數字的符號),將切割出來的字符串保存到字符數組中,並記錄有效行
2.將字符數組中的字符串轉換爲小寫格式(方便以後判斷和輸出)。循環判斷字數組符中的字符串前4個字符是不是小寫字母。若是是,則保存到哈希表中(方便字典序排序),並增長單詞數。若是不是,則捨去該字符串。
3.有哈希表中存儲的字符串,能夠方便的輸出單詞和詞頻。
4.算法思路:
獲取行數
將文件打開後,用readLine()函數逐行讀取文本內容並保存在fContent上,此時疊加行數。算法

獲取字符數
將字符串fContent讀取成功後,字符數+=fContent.length;windows

獲取單詞數
將fContent使用split(「\W+」)分割成只有可寫字符的單詞組存入String [] ch中,單詞數+=ch.length;數組

數據結構
使用HashMap保存單詞和使用頻率,不使用TreeMap的緣由是,TreeMap沒有自帶按值排序後,相同值按字典序排序的特性。而HashMap可使存儲、查找的時間效率都在O(1)內完成,而不是TreeMap的log(N);
值得注意的是,Map自己排序須要轉化成List,排序成功後因將結果應從新轉化爲LinkedHashMap。LinkedHashMap能夠按插入順序保存。數據結構

進階需求app

自定義輸入輸出
在類中額外保存輸入輸出名便可。函數

自定義詞頻統計輸出
在類中額外保存一個最大單詞數用來控制LinkedHashMap長度便可。

權重分析
在HashMap插值時,額外判斷是否來自Title,是的話記錄數+10,不然+1便可。

多參數的混合使用
讀取一行,依舊用split(「-「)函數分割成不一樣指令,分別調用函數便可。

從文件中獲得字符串

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 
            e.printStackTrace();
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }
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(6, fContent.length()-1 );
                        fByteCount += fContent.length();//
                        setMap(fContent,true);
                    }
                    else if(fContent.charAt(0) == 'A')
                    {
                        fContent = fContent.substring(9, fContent.length()-1 );//remove(Abstract: ) 
                        fByteCount += fContent.length();//
                        setMap(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]);
                    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 lib()
    {
        fileInput = "cvpr/result.txt";
    }
    public lib(String fName)
    {
        fileInput = fName;
        setWord();
    }
    public void setFileInput(String fName)
    {
        fileInput = fName;
    }
    public void setFileOutput(String fName)
    {
        fileOutput = fName;
    }
    public void setWValue(int num)
    {
        wValue = num > 0 ? true : false;
    }
    public void setMaxWordNum(int num)
    {
        if(num >= 0)    maxWordNum = num;
    }
    public int getFWordCount()
    {
        return fWordCount;
    }
    public int getFRowCount()
    {
        return fRowCount;
    }
    public int getfByteCount()
    {
        return fByteCount;
    }
    public int getMaxWordNum()
    {
        return maxWordNum;
    }
    public int getMaxWordNum(int num)
    {
        return maxWordNum = num;
    }
    public boolean isLower(char c)
    {
        return (c>='a' && c<='z');
    }
    public boolean isDigit(char c)
    {
        return c<='0' && c<='9';
    }

爬蟲實現

爬蟲實現是我本身一步一步的,從沒據說過,到查資料,寫代碼,最後是輸出 result.txt文件,用了三個晚上加一個通宵,和其餘一些零零散散的時間。
怎麼說呢!仍是先描述一下學習的過程吧!由於用得時間多,學得也多嘛!
週一(3月11日)晚上,開始學習爬蟲之旅。有些惶恐,也有些期待。當時以爲這個爬蟲技術學會了,之後會有大用處。就像一個朋友開玩笑說:「學了爬蟲,之後均可以本身上起點小說網爬取小說內容了!」
我記得一開始百度搜索的時候,爬蟲軟件不少,我下載了一個叫「后羿」的爬蟲軟件,不過沒用上,由於要學習後本身編碼!
學習的過程,大可能是重複歷史。從網上找一些具體的代碼例子,看看別人怎麼寫代碼,又實現了哪些功能。
而我學習的過程,借鑑了五份代碼。都是一份一份的從網上查找,選取感受適合的。而後一邊敲寫,一邊理解。
第一份:關於URL連接輸出HTML所有信息。從這份中,我學到了URL連接以及獲取網頁html的內容。
第二份:從一段字符串中,使用正則表達式截取電話號碼。(當時還不知道正則表達式,只記得搜索相關資料的時候看見網上有人表示:「正則表達式寫錯了一點點,後面就所有錯了。」因而,謹慎細微,不敢擅自修改樣例中的正則表達式。但最後在本身理解正則表達式後發現,這好像也不是當時想的那麼難的。)
第三份:從單個網頁中截取須要的信息(用正則表達式)。當時在裏的時候,就是很謹慎的,正則表達式和對應網頁上的信息看了一遍又一遍,勉強理解了,但幾乎不會用。直到把正則表達式的規則看了許多遍,才豁然開朗。
第四份:是關於讀取從第一個頁面出發,而後讀取不少頁面的連接以及連接裏的內容。這份樣例代碼很少,但當時理解起來很繁瑣,頭都要炸了
第五份:和第三份同樣,是從單個網頁中截取須要的信息。我最後完成的代碼,大部分格式都是模仿這一份的。
完成篇:代碼爬取出論文後,依舊還有兩個改進,這個下面優化的時候會講。

爬蟲實現:

1.獲取一個頁面上的標題和摘要信息

private static String  htmlFiter(String html,int num) 
        {
            StringBuffer buffer = new StringBuffer();
          
            String str1= "";
            String str2= "";            
                // 匹配Title(題目),題目被包含在<div id="papertitle"> 和 </div>中
                Pattern p1 = Pattern.compile("(<div id=\"papertitle\">)(.+?)(</div>*)");            
                Matcher m1 = p1.matcher(html);  
                // 匹配Abstract(摘要),摘要被包含在<div id="abstract"> 和 </div>中
                Pattern p2 = Pattern.compile("(\"abstract\")(.+?)(</div>*)");
                Matcher m2 = p2.matcher(html);
                if(m1.find() && m2.find()) 
                {
                    
                    str1 = m1.group(2);
                    str1 = num+"\r\n"+"Title: "+str1+"\r\n";
            //      buffer.append("\nTitle: ");
        //          buffer.append(str1);
                    str2 = m2.group(2);
                    str2 = str2.replace(">","");
                    str2 = "Abstract:"+str2+"\r\n"+"\r\n";
       //           buffer.append("\nAbstract: ");
        //          buffer.append(str2);
        //          buffer.append("\n");
                    
            try
            {
                 File file = new File("D:\\result.txt");                         
                 FileWriter fw = new FileWriter(file,true);
                 String str5 = str1;
                 fw.write(str5);
                 String str6 = str2 + System.getProperty("line.separator");
                 fw.write(str6);
                   
                   fw.close();
            }catch(Exception e) {
                e.printStackTrace();
            }
                }       
            return buffer.toString();
        }

2.獲取主頁上論文的連接信息。調用第一個類,接入連接信息,爬取頁面上的標題和摘要信息。並保存到result.txt文檔中

private static String  htmlFiter(String html,int num) 
        {
            StringBuffer buffer = new StringBuffer();
            pre_2 p2 = new pre_2();
            
            String str = null;
            Pattern p = Pattern.compile("(<div id=\"content\">)(.*)(</div>*)");         
        Matcher m = p.matcher(html);            
        if(m.find()) 
        {
            str = m.group(2);
            String str1 = null;
            String str2 = null;
            Pattern p1 = Pattern.compile("(class=\"ptitle\">)(.+?)(a href=\")(content_cvpr_2018)(.+?)(_2018_paper.html\">)");           
            Matcher m1 = p1.matcher(str);
    
            while(m1.find())
            {
                
                str1 = m1.group(5);
                str2 = ("http://openaccess.thecvf.com/content_cvpr_2018"+str1+"_2018_paper.html");
                buffer.append(str2);
                buffer.append("\n");
                p2.getTodayTemperatureInfo(str2,num); 
                num++;
            }
        }                   
            return buffer.toString();
        }

3.主函數

public class pre_main 
{
    pre_1 p1 = new pre_1();
    String info = p1.getTodayTemperatureInfo("http://openaccess.thecvf.com/CVPR2018.py");
}

感言:

就像一道門,在你走進去以前,你會胡亂猜想甚至懼怕。由於你不知道門裏面是什麼,裏面對於你來講是黑漆漆的一片。而人生來就對未知的東西有着期待和恐懼。
待你真正走進這道門的時候,你感嘆一聲:「原來如此。」
當時我完成截取論文的爬蟲代碼的時候,用了三個晚上加一個通宵。但如今,你讓我再爬取難度類似的文檔時,我只要十分鐘。這大概就是師傅領進門的重要性吧!

4、改進的思路

(1)當時,我開始完成爬蟲代碼的時候,一共有兩個程序。第一個:爬取主頁中,關於論文連接的連接地址,保存到一個名叫 1.txt 文檔中。第二個:從 1.txt 文檔中,獲取連接地址,再從相應的地址獲取須要的論文標題和內容信息,保存到result.txt。
第一次完成的時候,截取的連接地址有兩個不符合要求,改了不少次正則表達式,甚至把對的改錯了!最後發現的問題是:有兩個連接的地址中後面的 「CVPR」是小寫,其餘的是大寫。發現這個的時候很開心,由於究竟了許久,最後在逐字對照正確爬取和錯誤爬取連接的時候發現。
(2)已經爬取了result.txt 後,還有一個問題是,會產生一個1.txt 文件。因而便須要優化,優化即是把上面兩個程序都封裝成包,最後在主程序中調用,既解決了產生多餘 1.txt 的要求,也知足了測試要求。

正確爬取結果

5、關鍵代碼

1.從文件中讀取字符流到緩衝區,判斷緩衝區的每一行字符,切割字符串(去掉非字母非數字的符號),將切割出來的字符串保存到字符數組中,並記錄有效行

2.將字符數組中的字符串轉換爲小寫格式(方便以後判斷和輸出)。循環判斷字數組符中的字符串前4個字符是不是小寫字母。若是是,則保存到哈希表中(方便字典序排序),並增長單詞數。若是不是,則捨去該字符串。

3.有哈希表中存儲的字符串,能夠方便的輸出單詞和詞頻。

從文件中獲得字符串

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 
            e.printStackTrace();
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }
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(6, fContent.length()-1 );
                        fByteCount += fContent.length();//
                        setMap(fContent,true);
                    }
                    else if(fContent.charAt(0) == 'A')
                    {
                        fContent = fContent.substring(9, fContent.length()-1 );//remove(Abstract: ) 
                        fByteCount += fContent.length();//
                        setMap(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]);
                    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;
    }

6、關於隊友

隊友很強!我也會努力!

隊友在代碼實現和優化方面,很值得我學習。就好比說,在基礎需求的字典排序中,我還在思考是否是用字符數組保存字符串後,比較相同字符串,而後進行字典排序。這樣的方法,想一想就很麻煩!而隊友已經想到用哈希表保存數據,不管是字典安排序和輸出,都很方便。
其次,此次做業在兩人的溝通上遠不如上一次,做業類型不一樣是一個緣由。還有緣由是,我得增強代碼能力,但願能作到和隊友基本同步。

7、總結

在一番辛勤勞動以及一份充碩的收穫以後,感悟頗多!
當時在閱讀理解同伴的基礎需求代碼上就有一些想法,如今補上。
大學已經三年了!過去的時光零零散散,孤獨的,寂寞的,大笑的,啜泣的。不管之前如何,如今都須要將心境沉浸下來了!而現在,有一個優秀的同伴,我很幸運。就像一樣學過數據結構,同伴能靈活運用,而我仍是處於老舊的思想。我要學習這樣的思惟方法,這一次的做業,是一個好的開端,但願能繼續下去。
內心的感悟頗多,但到了嘴邊,好像一切都簡單了!
簡單便簡單罷!再說一說自學的感覺。當一座你恐懼的高山被你踩再腳下時,再看看四周,便有「會當凌絕頂,一覽衆山小」的豪情,心中無限舒暢。就算當初學習過程當中,陷入困境時,也曾無奈。掙扎着,想要抓住什麼看成救命稻草。會發現,能依靠的有朋友,有本身。
當匆忙時,會過得很充實。偶爾停下腳步,會發現,這一刻之前忽略了的閒暇,是那麼的美好舒服。

8、發現問題

總結了收穫,也有一些問題發現。 第一個是關於交流討論和實踐編碼的 上一次做業中,交流討論的時間不少,也從討論中明確了工做的需求和目的。在實際工做的時候,能夠引用一個成語就是:成竹在胸。 但此次做業中,一但涉及編寫代碼,討論的時間就會減小不少。並且當同伴寫出代碼後,就不知道從哪裏插手,好像本身修改就會破壞了同伴代碼的完整性。 這裏須要助教老師給咱們解答一下疑惑。 第二個是關於github使用的 老實說,由於之前沒有接觸過,並且是全英文的。根據老師的做業要求,fork 了連接,新建文件夾以及Pull Request ,等等操做後,不知道是否正確。我當時是創建了一個文件,而後把完成的代碼複製粘貼上去,選擇了new Pull Request。但後面又通過同窗的講解,好像要下載一個github,而後克隆文件什麼的。操做起來依舊是不知方向。 而我想向老師提議一下,若是下次有什麼新的網站啊,仍是軟件什麼須要用到。能不能像不少百度指導同樣,給一些圖片加上紅色箭頭標記,這樣能讓咱們快速的熟悉運用這些網站。也許在熟練的使用者眼裏,這只是簡單的操做,但對於初學者來講,是莫大的幫助了!

相關文章
相關標籤/搜索