天天看點

軟工實踐結對作業二

軟體工程結對作業二

結對隊友、部落格連結、GitHub項目位址、分工

  • 作業連結 https://edu.cnblogs.com/campus/fzu/FZUSoftwareEngineering1816W/homework/2160
  • 結對成員
    • 姚志輝:031602142
      • 部落格位址:http://www.cnblogs.com/52wu244/
      • 分工:負責項目各個子產品功能的實作
    • 王龍濤:031602134
      • 部落格位址:https://www.cnblogs.com/wang371091997/p/9781183.html
      • 分工:負責爬取部分,附加題部分,一起Debug,解決問題
  • GitHub位址:https://github.com/xiaozhirensan/PairProject-C

PSP表格

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

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

爬蟲使用

  • 我的python爬蟲學習過程:https://blog.csdn.net/FZUMRWANG/article/details/82944100
  • 我的思路
    軟工實踐結對作業二
import requests
from bs4 import BeautifulSoup #引入BeautifulSoup子產品
i=0
res = requests.get('http://openaccess.thecvf.com/CVPR2018.py') #通過監聽網頁可知使用get方法
res.encoding='utf-8' #若是有中文則需要加上utf-8編碼
soup = BeautifulSoup(res.text,'html.parser') #将get到的内容放入BeautifulSoup包中,并且使用html.parser解析由requests.get所得到的html頁面内容
head='http://openaccess.thecvf.com/' #由于所得到的連結可能不全,是以加上前面的總連結,有時候不用
for news in soup.select('.ptitle'): #ptitle是通過觀察每一個标題的分隔符而得到的,通過Chrome的檢查功能中的選擇功能來選擇标題然後何可看出每一個标題是使用ptitle分隔,不同網頁可能不同,如果ptitle是class的話用.,如果是id的話用#
    if len(news.select('a'))>0: #由于得到的list可能為空,是以加此判斷
        a=head+news.select('a')[0]['href'] #選擇ptitle下<a>标簽中的href連結
        #print(h2,head+a)
        res2 = requests.get(a) #通過get方法得到網址的回應
        res.encoding='utf-8'
        soup2 = BeautifulSoup(res2.text,'html.parser')
        h2=soup2.select('#papertitle')[0].text.strip()
        article=soup2.select('#abstract')[0].text.strip()
        #print('Title:',h2)
        #print('Abstract:',article)
        with open('D:\\result.txt','a',encoding='gb18030',errors='ignore') as f:
            f.write(str(i))
            f.write('\n')
            f.write('Title: '+h2)
            f.write('\n')
            f.write('Abstract: '+article)
            f.write('\n')
            f.write('\n')
            f.write('\n')
        i=i+1
        
           
  • 我爬取的結果
軟工實踐結對作業二
  • 爬取結果附件:https://files.cnblogs.com/files/wang371091997/result.zip

代碼組織與内部設計實作&&算法流程圖

軟工實踐結對作業二

說明算法的關鍵

  • 單詞統計:隻有連續四個是字母就是一個單詞,有分隔符隔開就重新開始判斷,并且将單詞加入Hash,然後通過判斷w以及單詞所在的位置将value值+10或者+1
  • 詞組統計:在單詞判斷的基礎之上進行詞組的統計,即将m個單詞以及單詞之間的分隔符加入詞組,并且将詞組加入Hash,然後通過判斷w以及詞組所在的位置将value值+10或者+1
  • 行數統計:直接使用getline按行讀取,然後去除掉不在統計範圍内的行即可
  • 權值的計算:就如同上面的單詞和詞組統計,判斷w然後Hash中value+10或者+1

關鍵代碼解釋

  • 指令行解釋類
struct Command {
	bool _i;		//是否按照指定路徑讀入檔案
	bool _o;		//是否按照指定路徑讀出檔案
	bool _w;		//是否加入詞頻權重統計
	bool _m;		//是否開啟詞組詞頻統計功能
	bool _n;		//是否開啟自定義詞頻統計輸出
	char inFile[MAX_PATH_LENGTH];		//讀入檔案路徑 
	char outFile[MAX_PATH_LENGTH];      //讀出結果路徑
	int m;          //詞組中單詞數
	int n;          //數組數
	Command() {
		_i = false;
		_o = false;
		_w = false;
		_m = false;
		_n = false;
		strcpy_s(inFile, "input.txt");  //将初始讀入檔案設定為input.txt
		strcpy_s(outFile, "output.txt");	//将初始讀出檔案設定為result.txt
		m = 1;
		n = 10;
	}
	void commandAnalyse(char commandStr[], Command &command);
	int swiftNumber(char str[]);
};
           

Command類用于解析使用者輸入的指令行,将自定義參數設定為bool型變量,同時給各參數賦予初值,第一次寫的代碼純粹為一個函數,各種變量紛雜,可讀性很差。經過改進之後,此程式可移植性高,結構清晰,且封裝較好。

  • 詞組頻數統計函數
void WordList::wordCount(string fileName, WordList &wordList, int m, bool _w)
{
	char word[MAX_WORD_LENGTH] = { 0 };
	char wordStr[2000] = { 0 };
	string str;
	ifstream inFile;
	inFile >> noskipws;
	inFile.open(fileName);
	int wordposition = 0;
	int wordPosition = 0;
	char c;
	int delta = 'a' - 'A';
	int i = 0, j = 0; //記錄字元目前位置
	int n = m;     
	while (getline(inFile, str))
	{
		if (str[0] != 'T'&&str[0] != 'A')
			continue;
		c = str[0];
		while (c != '\0')
		{
			c = str[i];
			if (c <= 'Z'&&c >= 'A') c += delta;
			bool separator1 = (c >= 'a'&&c <= 'z');
			bool separator2 = (c >= '0'&&c <= '9');
			if (separator1)
			{
				wordposition++;
				wordStr[wordPosition] = c;
				wordPosition++;
			}
			if (separator2)
			{
				if (wordposition < 4)
				{
                    memset(wordStr, '\0', sizeof(wordStr));
					wordposition = 0;
					wordPosition = 0;
				}
				else
				{
					wordStr[wordPosition] = c;
					wordPosition++;
				}
			}
			if (!separator1 && !separator2 && wordposition < 4)
			{
				
				memset(wordStr, '\0', sizeof(wordStr));
				memset(word, '\0', sizeof(word));
				n = m;
				wordPosition = 0;
				wordposition = 0;
				j = i;
			}
			if (c==':' && wordposition >= 4 && strcmp(wordStr, "title") == 0)
			{
				state = 1;
				wordPosition = 0;
				wordposition = 0;
				memset(wordStr, '\0', sizeof(wordStr));
			}
			if (c == ':' && wordposition >= 4 && strcmp(wordStr, "abstract") == 0)
			{
				state = 2;
				wordPosition = 0;
				wordposition = 0;
				memset(wordStr, '\0', sizeof(wordStr));
			}
			if (!separator1 && !separator2 && wordposition >= 4 && n >= 1)
			{
				if (n > 1)
				{
					wordStr[wordPosition] = c;
				}
				strcat_s(word, wordStr);
				memset(wordStr, '\0', sizeof(wordStr));
				if (n == m)
					j = i;
				if (n == 1)
				{
					wordList.addWord(word, _w);
					memset(word, '\0', sizeof(word));
					n = m+1;
					i = j;
				}
				wordPosition = 0;
				wordposition = 0;
				n--;
			}
			i++;
		} 
	     memset(word, '\0', sizeof(word));
	     memset(wordStr, '\0', sizeof(wordStr));
	   i = 0;
	}
	inFile.close();
}
           

此函數為單詞處理類WordList中的一個函數,功能為抽取單詞,并将文本字元串轉換為詞組存儲進傳入連結表,這部分花費了較多時間并進行了多次改進。

代碼簽入記錄

軟工實踐結對作業二

附加題設計與展示

  • 爬取了論文作者和論文時間
    • 文本連結:https://files.cnblogs.com/files/wang371091997/author.zip
    • 結果截圖:
軟工實踐結對作業二
  • 統計2018年熱度最高的十個名詞并且制作成餅圖展示資料
    • 餅圖連結:http://myecharts.applinzi.com/bingtu.html
    • 結果展示
軟工實踐結對作業二
  • 可視化代碼
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ECharts</title>
    <!-- 引入 echarts.js -->
    <script src="js/echarts.js"></script>
</head>
<body>
    <!-- 為ECharts準備一個具備大小(寬高)的Dom -->
    <div id="main" style="width: 1200px;height:500px;"></div>
    <script type="text/javascript">
        // 基于準備好的dom,初始化echarts執行個體
        var myChart = echarts.init(document.getElementById('main'));

        // 指定圖表的配置項和資料
        
		option = {
		    title : {
		        text: 'CVPR論文熱詞',
		        subtext: '2018年',
		        x:'center'
		    },
		    tooltip : {
		        trigger: 'item',
		        formatter: "{a} <br/>{b} : {c} ({d}%)"
		    },
		    legend: {
		        orient : 'vertical',
		        x : 'left',
		        data:['image','network','learning','model','method','deep','data','approach','vedio','papper']
		    },
		    toolbox: {
		        show : true,
		        feature : {
		            mark : {show: true},
		            dataView : {show: true, readOnly: false},
		            magicType : {
		                show: true, 
		                type: ['pie', 'funnel'],
		                option: {
		                    funnel: {
		                        x: '25%',
		                        width: '50%',
		                        funnelAlign: 'left',
		                        max: 1548
		                    }
		                }
		            },
		            restore : {show: true},
		            saveAsImage : {show: true}
		        }
		    },
		    calculable : true,
		    series : [
		        {
		            name:'通路來源',
		            type:'pie',
		            radius : '55%',
		            center: ['50%', '60%'],
		            data:[
		                {value:1224, name:'image'},
		                {value:973, name:'network'},
		                {value:971, name:'learning'},
		                {value:762, name:'model'},
		                {value:662, name:'method'},
		                {value:640, name:'deep'},
		                {value:521, name:'data'},
		                {value:500, name:'approach'},
		                {value:475, name:'vedio'},
		                {value:474, name:'paper'}
		            ]
		        }
		    ]
		};
        // 使用剛指定的配置項和資料顯示圖表。
        myChart.setOption(option);
    </script>
</body>
</html>
           

性能分析與改進

  • 描述你改進的思路

改進思路可歸納為三點

  • 第一,将指令行解析函數抽象為一個類,類包括屬性和方法,變量抽象為屬性,字元串解析函數為方法。
  • 第二,将單詞加傳入連結表時,啟用hash函數形成單詞詞表,将每次添加如連結清單的單詞利用其ASCCI碼值抽象為一個數值,這樣可以快速找到所需單詞。
  • 第三,将逐個字元讀取檔案轉換為逐行讀取檔案,這樣處理字元串會更加友善,減少了代碼量。
  • 展示性能分析圖和程式中消耗最大的函數
void WordList::wordCount(string fileName, WordList &wordList, int m, bool _w)
{
	char word[MAX_WORD_LENGTH] = { 0 };
	char wordStr[2000] = { 0 };
	string str;
	ifstream inFile;
	inFile >> noskipws;
	inFile.open(fileName);
	int wordposition = 0;
	int wordPosition = 0;
	char c;
	int delta = 'a' - 'A';
	int i = 0, j = 0; //記錄字元目前位置
	int n = m;     
	while (getline(inFile, str))
	{
		if (str[0] != 'T'&&str[0] != 'A')
			continue;
		c = str[0];
		while (c != '\0')
		{
			c = str[i];
			if (c <= 'Z'&&c >= 'A') c += delta;
			bool separator1 = (c >= 'a'&&c <= 'z');
			bool separator2 = (c >= '0'&&c <= '9');
			if (separator1)
			{
				wordposition++;
				wordStr[wordPosition] = c;
				wordPosition++;
			}
			if (separator2)
			{
				if (wordposition < 4)
				{
                    memset(wordStr, '\0', sizeof(wordStr));
					wordposition = 0;
					wordPosition = 0;
				}
				else
				{
					wordStr[wordPosition] = c;
					wordPosition++;
				}
			}
			if (!separator1 && !separator2 && wordposition < 4)
			{
				
				memset(wordStr, '\0', sizeof(wordStr));
				memset(word, '\0', sizeof(word));
				n = m;
				wordPosition = 0;
				wordposition = 0;
				j = i;
			}
			if (c==':' && wordposition >= 4 && strcmp(wordStr, "title") == 0)
			{
				state = 1;
				wordPosition = 0;
				wordposition = 0;
				memset(wordStr, '\0', sizeof(wordStr));
			}
			if (c == ':' && wordposition >= 4 && strcmp(wordStr, "abstract") == 0)
			{
				state = 2;
				wordPosition = 0;
				wordposition = 0;
				memset(wordStr, '\0', sizeof(wordStr));
			}
			if (!separator1 && !separator2 && wordposition >= 4 && n >= 1)
			{
				if (n > 1)
				{
					wordStr[wordPosition] = c;
				}
				strcat_s(word, wordStr);
				memset(wordStr, '\0', sizeof(wordStr));
				if (n == m)
					j = i;
				if (n == 1)
				{
					wordList.addWord(word, _w);
					memset(word, '\0', sizeof(word));
					n = m+1;
					i = j;
				}
				wordPosition = 0;
				wordposition = 0;
				n--;
			}
			i++;
		} 
	     memset(word, '\0', sizeof(word));
	     memset(wordStr, '\0', sizeof(wordStr));
	   i = 0;
	}
	inFile.close();
}
           

此函數為功能為抽取合法詞組,是最消耗時間的函數段。

void WordList::addWord(char word[],bool _w)
{
	//将word這個單詞添加到詞頻統計表中(或者詞頻+1)
	int  p_index = Hash(word);
	WordIndex* pIndex = index[p_index];
	while (pIndex != nullptr)
	{
		Word *pWord = pIndex->pWord;
		if (!strcmp(word, pWord->word))
		{
			if (_w == true && state == 1)
				pWord->num += 10;
			else
				pWord->num++;
			Word *qWord = pWord->previous;
			while (qWord->num < pWord->num) 
			{
				if (qWord == pWordHead) return;
				shiftWord(pWord);

				qWord = pWord->previous;
			}
			while (strcmp(qWord->word, pWord->word) > 0) 
			{
				if (qWord->num > pWord->num) return;
				shiftWord(pWord);
				qWord = pWord->previous;
			}
			return;
		}
		pIndex = pIndex->next;
	}
	Word *pWord;
	if (_w == true && state == 1)
		pWord = new Word(word, 10);
	else
		pWord = new Word(word, 1);
	pWord->previous = pWordTail->previous;
	pWord->next = pWordTail;

	pWordTail->previous->next = pWord;
	pWordTail->previous = pWord;

	pIndex = new WordIndex(pWord, index[p_index]);
	index[p_index] = pIndex;

	Word *qWord = pWord->previous;
	while (strcmp(qWord->word, pWord->word) > 0) {
		if (qWord->num > pWord->num) return;
		shiftWord(pWord);
		qWord = pWord->previous;
	}
}
           

此函數功能為開啟連結清單,存儲詞組,是空間消耗最大的函數。

遇到代碼子產品異常或者結對困難及解決方法

  • 問題描述
    • 問題1:無法識别辨別符
    • 問題2:執行之後某個檔案無法使用
    • 問題3:執行大文本檔案之後不出現越界終端
  • 做過哪些嘗試
    • 問題1嘗試:解除安裝了vs......然後重裝,i am sorry,我就是這麼特立獨行的蠢,然後在網絡上查找資料
    • 問題2嘗試:不斷的查找錯誤,修改代碼,換使用函數,在網絡上查找解決方法
    • 問題3嘗試:改變讀取方法,将字元數組改成字元串
  • 是否解決
    • 問題1:已解決,是因為無法識别頭檔案,将頭檔案路徑更新即可
    • 問題2:已解決,是因為......我們删除并且改變使用函數之後解決了
    • 問題3:已解決,是因為我們開的數組不夠大
  • 有何收獲
    • 解決的問題的過程中我又get到一種排查錯誤的方法
    • 增強自己的抗壓能力
    • 明白了程式員的真正意義,我們笑談:“我一直把頭藏在帽子裡是為了遮擋我頭上的鳥窩,并且思考着我的代碼”,這個梗我隻有我和隊友get到......

評價你的隊友

  • 值得學習的地方:隊友很能熬夜,我熬不過他,他能熬到五點,我隻能熬到四點;他的抗壓能力很強,能夠在緊張的代碼之餘還能夠玩場遊戲,然後問題就解決了,這告訴我們,學習的時候可以适當放松,他有堅韌不拔的意志,這是我值得學習的地方
  • 需要改進的地方:希望他愛惜身體,不要熬夜太晚,隻要熬到四點就好了,不用五點的
  • 我們需要改進的地方:我們一開始看完題目,腦中想了一下,OK,很簡單,應該花不多時間,是以沒有特别的重視,然後,我們開始上手的時候,竟然會出現各種問題,困擾了我們很久,是以,任何事情還是先做為好,很多事情不是以人意志為轉移的

學習進度條

  • python爬蟲:18/10/3
  • 結對項目開發:18/10/8-18/10/12