軟工-結對做業2

軟工1816 · 第五次做業 - 結對做業2

優秀的結對隊友:樂忠豪html


分工明細

  • 分工以下
    • 蔡子陽:完成所有爬蟲實現及附加題所有內容
    • 樂忠豪:使用C++實現其他需求功能

PSP表格

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

解題思路描述與設計實現說明

1.爬蟲實現及使用

  • 用python代碼編寫爬蟲

首先先對論文列表網頁爬取每篇論文的網址python

def getWebInfo(url):#獲取網頁中的內容
    try:
        r = requests.get(url)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
    except:
        print("request error")
    demo = r.text
    soup = BeautifulSoup(demo,"html.parser")
    return soup
    
url = "http://openaccess.thecvf.com/CVPR2018.py"
soup = getWebInfo(url)
papersInfo = soup.find_all('dt')#每篇論文的網址造成的list

而後對每一篇論文的網頁爬取相對應信息並進行正則表達式去除網頁標籤最後得到標題和摘要並寫入到文件中git

def getPaperInfo(paperLink):#獲取論文主頁的內容
    url = "http://openaccess.thecvf.com/" + paperLink
    soup = getWebInfo(url)
    re_h=re.compile('</?\w+[^>]*>')#HTML標籤
    #利用正則表達式去除網頁內容中的標籤
    title =re_h.sub('', str(soup.find('div',id="papertitle"))).strip()
    abstract = re_h.sub('',str(soup.find('div',id="abstract"))).strip()
    author = re_h.sub('',str(soup.find('i'))).strip().split(', ')
    return title,abstract,author
    
with open(filename,'w',encoding='utf-8') as outfile:
    for paperInfo in papersInfo:
        title,abstract,author = getPaperInfo(paperInfo.find('a').get('href'))
        outfile.write(str(i)+'\r\n')
        i = i+1
        outfile.write('Title: '+title+'\r\n')
        outfile.write('Abstract: '+abstract+'\r\n\r\n\r\n')

2.代碼組織與內部實現設計

  • C++部分:原先計劃使用一個論文類存儲論文信息,可是在需求分析的時候發現此次的做業並不須要存儲論文信息,因此直接使用字符串對文本進行分析統計。
    • 結構圖以下:github

    • 組織結構:根據需求實現4個功能(詞頻統計、行數統計、單詞數統計、字符數統計),共4個頭文件分別實現。函數接口以下:正則表達式

int CountWords(char *filename)    //統計單詞數
int CountLines(char *filename)    //統計行數
int CountChars(char *filename)    //統計字符數
void CountWf(char *filename, ofstream &fout,char* outfile,int topNum,int itemLenth,int weight_Pid)    
//詞頻統計,topNum爲輸出的詞項數目,itemLenth爲詞項長度,weight_Pid爲標題權重開關

3.說明算法的關鍵與關鍵實現部分流程圖

  • C++關鍵算法:
    • 算法說明:
      • 除了詞頻統計外其他三個函數實現簡單,只需注意細節便可(如Title: 等不做爲單詞及字符看待等)。
      • 詞頻統計和上次我的項目大同小異,用map容器實現,並且其實現方式爲紅黑二叉樹,存儲效率不低,複雜度爲O(nlogn),針對排序使用vector容器輔助堆排序實現。要求輸出前K個詞項,時間複雜度爲O(nlogK),
      • 與上次做業有所不一樣的是不只僅是單詞,而是詞項的統計,這裏引入list隊列容器,檢測到一個合法單詞則入隊尾,若符合詞項要求則輸出隊列內容,並令隊首出隊列。若碰到不合法單詞則將當前隊列清空。時間複雜度爲O(n),與單詞的詞頻統計耗費時間相差無幾。
    • 詞頻統計關鍵代碼及流程圖展現:
//爲了節省篇幅僅展現部分代碼
void CountWF(char *filename, ofstream &fout,char* outfile,int topNum,int itemLenth,int weigth_Pid)
{
    int num = 0;
    K = topNum;
    ifstream in(filename);          //  打開文件
    if (!in)
    {
        cerr << "沒法打開輸入文件" << endl;
        exit(0);
    }
    
    char readLine[100000];
    string tempLine;
    list<string> listString;
    
    while (in.getline(readLine, 100000))//逐行處理文件
    {

        tempLine = readLine;//存儲當前行的字符串
        if (tempLine.length()<6)  //論文序號
        {
            b.clear();
            continue;
        }
        else if (readLine[0] == 'T'&&readLine[1] == 'i'&&readLine[2] == 't'&&readLine[3] == 'l'&&readLine[4] == 'e')//Title字段
        {
            Count(tempLine, itemLenth, 7,weigth_Pid);
        }
        else if (readLine[0] == 'A'&&readLine[1] == 'b'&&readLine[2] == 's'&&readLine[3] == 't'&&readLine[4] == 'r'&&readLine[5] == 'a'&&readLine[6] == 'c'&&readLine[7] == 't')//Abstract字段
        {
            Count(tempLine, itemLenth, 10, weigth_Pid);
        }
        else if(readLine)
        {
            Count(tempLine, itemLenth, 0,weigth_Pid);
        }
    }
    if (essay.size() < K)K = essay.size();
    vector < map<string, int> ::iterator> top(K, essay.begin());
    topK(essay, top, fout,outfile);//堆排序算法

}

//隊列使用
list<string> b('\0');
list<string>::iterator it1;
if (char_Count >= 4) //如果合法單詞,則記錄
        {
            char words[100] = { "\0" };            //單詞存儲
            for (int k = i; k < i + count; k++)
            {
                words[k - i] = tempLine[k];

            }
            string s = words;//單詞存入字符串中
            b.push_back(s);   //推入隊列尾
            s = "\0";
            if (b.size() == MaxNum)//隊列滿
            {
                int nowNum = MaxNum;//當前隊列長度
                for (it1 = b.begin(); it1 != b.end(); it1++)
                {
                    s.append(*it1);
                    
                    nowNum--;
                    if (nowNum != 0)s.append(" ");
                }
                if(head==7&&weight_Pid==1)essay[s]+=10;//記錄詞組/單詞
                else essay[s]++;
                b.pop_front();  //隊首出隊列
                s = "\0";


            }

            i += count - 1;
        }
        else if (count > 0 && count < 4)  //若遇到不合法單詞狀況隊列
        {
            b.clear();
            i += count - 1;
        }
        else continue;

    }
    if (head == 7)b.clear(); //如果Title字段結束時狀況隊列

詞項/單詞 詞頻統計流程圖
算法


附加題設計與展現

  • 爬取做者信息

在getPaperInfo函數中加入以下代碼app

author = re_h.sub('',str(soup.find('i'))).strip().split(', ')

就能獲得做者的信息函數

image

  • 做者之間的聯繫圖

將每一個做者出現的次數寫入到文件中,造成點集工具

image

將同時出現的做者以及出現的次數寫入到文件中,以三元組形式,造成邊集性能

image

代碼以下

authors = []
i = 0
for paperInfo in papersInfo:#每篇論文的做者list的形式放入authors當中
    title,abstract,author = getPaperInfo(paperInfo.find('a').get('href'))
    authors.append(author)
for each in authors:#將同時出現的兩個做者造成元組,放入到字典當中,並進行計數
    for author1 in each:
        for author2 in each:
            if (author1,author2) not in relationships:
                relationships[(author1,author2)] = 1
            else:
                relationships[(author1,author2)] = relationships[(author1,author2)] + 1

有了點集和邊集後,用Gephi工具,將兩個文本文檔傳而後就能造成做者之間的聯繫圖了

image

這是總的做者聯繫圖,由於做者太多,因此會很密集

image

這是放大後的圖,每一個點都是一個做者,做者之間的聯繫粗細大小表明同時出現的頻率,越粗的線表明頻率越高

附件點這裏


性能分析與改進

  • 測試文本爲100w+字符數,花費時間在3s左右,運行結果以下:
  • VS的性能分析工具來看將檢測到的單詞/詞項存入map容器是最花時間的,分析結果以下:

爲了解決它想到以前將map改成unordered_map能減小一半的時間花費,可是此次花費時間差很少,結果以下:

除此以外其他花費時間較多的語句多爲判斷語句。。不知道應該怎麼再改進了。


單元測試

  • 構造測試數據思路
    • 測試用例包括做業上的兩個用例及對不一樣命令行參數的設置。
  • 很慚愧的一點是沒有使用VS寫單元測試代碼進行測試,僅僅手動改動輸入文件和命令行參數進行測試。測試函數包括四個需求(行數、字符數、單詞數、詞頻輸出)

Github的代碼簽入記錄

因爲此次做業是在上一次的基礎上進行改動,因此簽入次數較少,bug都是在最後關頭髮現的(實際上是原來就有而後想偷懶沒有改,最後良心發現才改了)


遇到的代碼模塊異常或結對困難及解決方法

  • 問題描述:C++程序沒法讀取爬蟲的結果文件而後程序崩潰
  • 作過的嘗試:
    • 發現爬蟲文件爲Unicode編碼,C++沒法識別,因此將文本改成Ascll編碼(沒有解決)
    • 發現讀取的文本Abstract字段無論有多少行都算做一行,嘗試自動添加換行符解決(失敗)
    • 最後發現是C++程序中讀取每行使用的Char[]容量太小沒法讀取Abstarct的所有內容致使崩潰,經過增大容量解決(暫時成功)
  • 經過各類嘗試終於解決啦
  • 收穫:細節決定成敗。。。可是其實盲目增大容量的作法是很不可取的,改成動態開闢空間應該會好不少。

個人隊友

個人隊友是個可靠的小夥伴,是個深藏不漏的大佬,是個6到飛起的人。


學習進度條(每週更新)

相關文章
相關標籤/搜索