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

課程連接 軟件工程實踐
做業要求 結對第二次—文獻摘要熱詞統計及進階需求
結對學號 221600425 221600429
做業目標 1.基本需求:實現一個能對文本文件中的單詞的詞頻進行統計的控制檯程序
2.進階需求:在基本需求實現的基礎上,編碼實現頂會熱詞統計器
Github項目地址 基本需求 進階需求
分工 221600425負責主要代碼編寫與測試,221600429負責爬蟲編寫和博客撰寫

PSP表格

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


解題思路描述



在拿到題目並進行理解後,咱們第一步即是想到使用正則表達式。可是因爲JAVA的已經封裝好,使用起來比較方便,並且C++須要用庫文件支持正則,這些庫的語法也是不一樣的,所以咱們選擇使用JAVA進行編程。在肯定了所使用的語言後,咱們經過課程連接所提供的博客以及百度搜索瞭解了完成做業所須要的基本技能(如GitHub的一些使用方法等)。其實在搞懂基本需求和進階需求的各個規則後,編程工做其實並非很難,由於之前寫過相似的程序;但咱們並無編寫過爬蟲程序,因此咱們會將·一部分時間花在爬蟲上面,主要經過CSDN和博客園上的博客進行學習。
html


設計實現過程



本次設計分爲兩個類,一個類用來存放實現功能的各類函數,另外一個類用來編寫主函數。實現功能的函數包括1.計算字符數個數的Lib.charNum(String)2.計算文本行數的
lineNum(String fileName)3.判斷是否爲單詞的isLetter(String letter)4,計算單詞總數的letterNum(String fileName)5.用例統計詞頻的getWordAndCount(String fileName,List list,List list1)
git

基本需求流程圖

單元測試設計

測試是檢查應用程序是不是工做按照要求,並確保在開發者水平,單元測試進入功能性的處理。單元測試是單一實體(類或方法)的測試。 單元測試在每個軟件公司開發高品質的產品給他們的客戶是十分必要的。Eclipse是直接對junit進行了集成的,咱們能夠直接用。首先編寫測試函數,而後咱們經過輸入一個測試文件進行單元測試。github

代碼組織與內部實現設計(類圖)

進階需求流程圖


程序性能改進



因爲能力有限,因此在性能分析中基本需求和進階需求中的文獻統計部分的性能改進的不是很明顯,所以這裏只展現爬蟲部分的
正則表達式

關於爬蟲程序的性能分析

優化前

優化前各方法耗時

優化後

線程

優化後各方法耗時
編程

消耗最大的函數多線程

for (Element elem : elems) {
                String url=elem.attr("href");
                threadPool.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Document doc = Jsoup.connect("http://openaccess.thecvf.com/" + url)
                                                .maxBodySize(0)
                                                .timeout(5000*60) // 設置鏈接超時時間
                                                .get();
                            
                            System.out.println(i+"\n"+doc.select("#papertitle").text() + "\n" + doc.select("#abstract").text() + "\n");
                            synchronized (fw) {
                                fw.append(
                                        (i++)+"\r\n"+
                                        "Title: "+
                                        doc.select("#papertitle").text() + "\r\n" +
                                        "Abstract: " +
                                        doc.select("#abstract").text() + "\r\n"
                                        +"\r\n\r\n");                                           
                                fw.flush();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }


關鍵代碼說明


基本需求關鍵代碼

getWordAndCount(String fileName,List list,List list1)//統計詞頻 app

public static void getWordAndCount(String fileName,List<String> list,List<Integer> list1) {
        Map<String,Integer> map = new HashMap();
        String regex="[^a-zA-Z0-9]";
        File srcFile=new File(fileName);
        try {
            FileReader fileReader=new FileReader(srcFile);
            BufferedReader bufferedReader=new BufferedReader(fileReader);
            String lineText=null;
            while((lineText=bufferedReader.readLine())!=null) {
                 String[] strs=lineText.split(regex);
                 for(int i=0;i<strs.length;i++) {
                     if(isLetter(strs[i])) {
                         strs[i]=strs[i].toLowerCase();
                         if(map.containsKey(strs[i])) {
                             map.put(strs[i], map.get(strs[i])+1);
                         }
                         else {
                             map.put(strs[i], 1);
                         }
                     }
                 }
            }
            String[] words=new String[map.size()];
            Integer[] counts=new Integer[map.size()];
        
            map.keySet().toArray(words);
            map.values().toArray(counts);
            
            for(int i=0;i<map.size();i++) {
                if(i<=9) {
                    for(int j=i;j<map.size();j++) {
                        if(counts[i]<counts[j]) {
                            Integer temp=null;
                            temp=counts[i];
                            counts[i]=counts[j];
                            counts[j]=temp;
                            String temp1=null;
                            temp1=words[i];
                            words[i]=words[j];
                            words[j]=temp1;
                        }
                        else if(counts[i]==counts[j]) {
                            if(words[i].compareTo(words[j])<0) {
                                Integer temp=null;
                                temp=counts[i];
                                counts[i]=counts[j];
                                counts[j]=temp;
                                String temp1=null;
                                temp1=words[i];
                                words[i]=words[j];
                                words[j]=temp1;
                            }
                        }
                    }
                }
            }
            for(int i=0;i<words.length;i++) {
                if(i>9) {
                    break;
                }
                else {
                    list.add(words[i]);
                }
            }
            for(int i=0;i<counts.length;i++) {
                if(i>9) {
                    break;
                }
                else {
                    list1.add(counts[i]);
                }
            }
            fileReader.close();
            bufferedReader.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

爬蟲程序關鍵代碼

fw=new FileWriter(new File("result.txt"));
Document doc = Jsoup.connect("http://openaccess.thecvf.com/CVPR2018.py")
                    .maxBodySize(0)
                    .timeout(1000*60)
                    .get();
Elements elems = doc.select(".ptitle a");

System.out.println(elems.size());
ExecutorService threadPool = Executors.newScheduledThreadPool(8);

爬蟲程序採用Jsoup進行html文檔的解析,首先鏈接到2018年的論文列表頁面,並經過設置鏈接時間和返回所測得的數據條數來保證爬取的初步成功。而後開設線程池,利用多線程來保證爬取的速度。ide

for (Element elem : elems) {
                String url=elem.attr("href");
                threadPool.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Document doc = Jsoup.connect("http://openaccess.thecvf.com/" + url)
                                                .maxBodySize(0)
                                                .timeout(5000*60) // 設置鏈接超時時間
                                                .get();
                            
                            System.out.println(i+"\n"+doc.select("#papertitle").text() + "\n" + doc.select("#abstract").text() + "\n");
                            synchronized (fw) {
                                fw.append(
                                        (i++)+"\r\n"+
                                        "Title: "+
                                        doc.select("#papertitle").text() + "\r\n" +
                                        "Abstract: " +
                                        doc.select("#abstract").text() + "\r\n"
                                        +"\r\n\r\n");                                           
                                fw.flush();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }

以上代碼經過初步爬取的信息進行深層次的爬取,主要原理是經過爬取論文列表的a標籤的href屬性進行進一步爬取,連接到href屬性的論文連接中,一樣設置鏈接時間以防連接超時而報錯,而後對爬取的html進行操做,主要是查找#papertitle和#abstract的內容並進行返回存入result.txt文件中。因爲使用多線程會致使出現髒數據和重複數據,所以在代碼中咱們使用了鎖來防止這一狀況。函數

進階需求關鍵代碼

進階部分的關鍵代碼中一樣有基本需求中的返回字符數、返回行數、判斷是否爲單詞、返回單詞數等的函數,所以再也不展現,僅展現統計詞頻的函數性能

public static void getWordFrequency(String fileName,List<String> list,List<Integer> list1,int Weightflag,int numFlag) {
        int value=0;
        if(Weightflag==0) {
            value=1;
        }
        else {
            value=10;
        }
        Map<String,Integer> map = new HashMap();
        String regex="[^a-zA-Z0-9]+";
        File srcFile=new File(fileName);
        try {   
            FileReader fileReader=new FileReader(srcFile);
            BufferedReader bufferedReader=new BufferedReader(fileReader);
            String lineText=null;
            while((lineText=bufferedReader.readLine())!=null) {
                if(lineText.contains("Title: ")) {
                    String temp=lineText.replaceFirst("Title: ", "");
                    //System.out.println(temp);
                    String[] strs=temp.split(regex);
                    for(int i=0;i<strs.length;i++) {
                         if(isLetter(strs[i])) {
                             strs[i]=strs[i].toLowerCase();
                             if(map.containsKey(strs[i])) {
                                 map.put(strs[i], map.get(strs[i])+value);
                             }
                             else {
                                 map.put(strs[i], value);
                             }
                         }
                     }
                }
                else if(lineText.contains("Abstract: ")){
                    String temp=lineText.replaceFirst("Abstract: ", "");
                    //System.out.println(temp);
                    String[] strs=temp.split(regex);
                    for(int i=0;i<strs.length;i++) {
                         if(isLetter(strs[i])) {
                             strs[i]=strs[i].toLowerCase();
                             if(map.containsKey(strs[i])) {
                                 map.put(strs[i], map.get(strs[i])+1);
                             }
                             else {
                                 map.put(strs[i], 1);
                             }
                         }
                     }
                }
                else {
                    continue;
                }
            }
            String[] words=new String[map.size()];
            Integer[] counts=new Integer[map.size()];
        
            map.keySet().toArray(words);
            map.values().toArray(counts);
            
            for(int i=0;i<map.size();i++) {
                if(i<numFlag) {
                    for(int j=i;j<map.size();j++) {
                        if(counts[i]<counts[j]) {
                            Integer temp=null;
                            temp=counts[i];
                            counts[i]=counts[j];
                            counts[j]=temp;
                            String temp1=null;
                            temp1=words[i];
                            words[i]=words[j];
                            words[j]=temp1;
                        }
                        else if(counts[i]==counts[j]) {
                            if(words[i].compareTo(words[j])<0) {
                                Integer temp=null;
                                temp=counts[i];
                                counts[i]=counts[j];
                                counts[j]=temp;
                                String temp1=null;
                                temp1=words[i];
                                words[i]=words[j];
                                words[j]=temp1;
                            }
                        }
                    }
                }
            }
            for(int i=0;i<words.length;i++) {
                if(i>numFlag-1) {
                    break;
                }
                else {
                    list.add(words[i]);
                }
            }
            for(int i=0;i<counts.length;i++) {
                if(i>numFlag-1) {
                    break;
                }
                else {
                    list1.add(counts[i]);
                }
            }
            fileReader.close();
            bufferedReader.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


部分單元測試代碼


測試是檢查應用程序是不是工做按照要求,並確保在開發者水平,單元測試進入功能性的處理。單元測試是單一實體(類或方法)的測試。 單元測試在每個軟件公司開發高品質的產品給他們的客戶是十分必要的。Eclipse是直接對junit進行了集成的,咱們能夠直接用,這也是咱們選擇Java的一個緣由。

代碼

class charNumTest { 

    @Test
    void test1() {
        System.out.println(new Lib().charNum("input1.txt"));
    }
    
    @Test
    void test2() {
        System.out.println(new Lib().letterNum("input1.txt"));
    }
    
    @Test
    void test3() {
        System.out.println(new Lib().lineNum("input1.txt"));
    }
    
}

以上代碼中charNum方法爲Lib類中的方法,能夠換成其餘方法,用以測試Lib中的各個單元方法。
包括:

Lib.charNum(String)//返回字符數
lineNum(String fileName)//返回行數
isLetter(String letter)//判斷是否爲單詞
letterNum(String fileName)//返回單詞數
getWordAndCount(String fileName,List<String> list,List<Integer> list1)//統計詞頻

運行結果


總結


問題及解決

在本次通過近一個星期的實踐中,咱們遇到了各類各樣的問題。第一個是正則表達式的編寫問題,利用Java的正則表達式能夠很方便地匹配輸入,同時這個問題也較爲簡單,經過百度能夠很容易地解決這個問題。第二個問題即是文獻統計的編寫。進行文獻的信息統計,須要想到各類可能性,漏算一種結果就會出錯,同時在這個狀況下也不容易查找出來,所以這個問題的難度就在於須要保證程序能夠處理全部的可能性。經過小隊兩我的的配合,以及和其餘小隊的合做,咱們也順利地解決這個問題。第三個問題即是爬蟲。在編寫爬蟲程序中,咱們出現了各類問題,其中主要有"connect time out"鏈接超時,爬取速度過慢,爬取出現髒數據等。針對第一個問題,咱們經過代碼增大了連接時間,第二個問題咱們採用了多線程來提升速度,但第二個問題引起了第三個問題,最後咱們採用加鎖的方式解決了髒數據。

評價

在本次實踐中,我和個人隊友各司其職,分工明確。個人隊友在實踐過程當中遇到問題不放棄,認真鑽研,經過努力解決了許多問題,在此過程當中沒有任何抱怨,十分刻苦,這正是我須要向他學習的地方。咱們本次合做在我看來雖有瑕疵但算成功,這天然和我隊友的努力是分不開的,在我看來他作的已經十分完美,並無須要改進的地方。

相關文章
相關標籤/搜索