• 課程名稱:軟件工程1916|W(福州大學)
• 做業要求:結對第二次—文獻摘要熱詞統計及進階需求
• 結對學號:041602421 翁昊 | 221600432 邱志勇
• 做業目標:
1、基本需求:實現一個可以對文本文件中的單詞的詞頻進行統計的控制檯程序。(完成)
2、進階需求:在基本需求實現的基礎上,編碼實現頂會熱詞統計器。(未完成)
git
翁昊:完成了部分代碼(行數計算,以及單詞爬取與排序函數)的編寫和調試和博客的編寫
邱志勇:完成了部分代碼的編寫(字符數計算,以及修改行數計算,以及單詞爬取與排序函數)和調試和博客的編寫,同時也完成了測試和代碼嵌入的部份內容
github
• 做業完成工具:vs 2017& github
• 博客編輯器: MARKDOWN
• PSP:文末
• github: https://github.com/teamwengqiu/PairProject1-C
正則表達式
(一)WordCount基本需求
實現一個命令行程序,不妨稱之爲wordCount。
統計文件的字符數:
• 只須要統計Ascii碼,漢字不需考慮
• 空格,水平製表符,換行符,均算字符編程
統計文件的單詞總數,單詞:至少以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。
英文字母: A-Z,a-z
字母數字符號:A-Z, a-z,0-9
分割符:空格,非字母數字符號
例:file123是一個單詞,123file不是一個單詞。file,File和FILE是同一個單詞
統計文件的有效行數:任何包含非空白字符的行,都須要統計。
統計文件中各單詞的出現次數,最終只輸出頻率最高的10個。頻率相同的單詞,優先輸出字典序靠前的單詞。
按照字典序輸出到文件result.txt:例如,windows95,windows98和windows2000同時出現時,則先輸出windows2000
輸出的單詞統一爲小寫格式
輸出的格式爲
characters: number
words: number
lines: number
<word1>: number
<word2>: numberwindows
(二)WordCount進階需求
新增功能,並在命令行程序中支持下述命令行參數。說明:字符總數統計、單詞總數統計、有效行統計要求與我的項目相同數組
整體上先完成大體的函數框架,再對框架進行細節填補和調試。
隨後WordCount基本需求:爬取單詞我採用輸入流ifstream每次讀單個字符,並用狀態機的方式分類各種型的字符串,從而實現爬取單詞。因爲爬取單詞代碼是依照狀態機思路完成的,測試數據主要是各種型字符串(開頭爲字母或者數字或者非字母數字,各類類型的中間過程,末尾的過濾)的爬取是否有沒有問題。以及檢測字典排序是否有問題。框架
整體類圖:
關於最困難的單詞分析函數會在下面代碼中解釋。
單元測試的設計:查閱並學習單元測試的方法,讀文件數據,在判斷程序調用類中函數進行相等斷言判斷。
• 階段一
o 共同討論WordCount的基本功能需求
o 代碼基本實現WordCount的基本需求
o 將項目各個功能分離成獨立函數並進行相應的測試調整
o 對代碼進行相應的測試和調整優化
• 階段二
o 將項目按照做業格式上傳github
o 撰寫博客編輯器
WordCount基本需求
爬取單詞我採用輸入流ifstream每次讀單個字符,並用狀態機的方式分類各種型的字符串,從而實現爬取單詞。函數
選擇了實際存儲模式爲哈希表的unordered_map,由於在爬取單詞時並不須要隨時更新排序,而最終目標是選出top10,因此咱們考慮得出在爬取完成後不採起整個排序而是,採起遍歷原數據,並新設一個10大小結構數組,本身設計比較大小的函數,對每一個新遍歷元素,進行在10大小結構數組冒泡排序,這樣平均複雜度是o(10n)。 //爬取單詞 int TextAnalysisUtil::wordAnalysis(char * fileName, WordInf * tempArr) { ifstream fin(fileName); if (!fin) { cout << "cuowu"; return -1; } char ch; string str; int state = 0; int nflag; int wordCount = 0; unordered_map<string, int> map; while (fin.get(ch)) { switch (state) { case 0: //查找單詞首的狀態 if (isdigit(ch)) { //轉入找到數字開頭的過濾狀態 state = 1; } else if (isalpha(ch)) { //找到單詞,轉入爬取單詞狀態 state = 2; str = ch; nflag = 1;//記錄單詞開頭字母數 } break; case 1: //轉入找到數字開頭或不符合單詞開頭有4個字母的過濾狀態 if (!(isdigit(ch)||isalpha(ch))) { state = 0; } break; case 2: //爬取單詞狀態 if (isalpha(ch)) { ++nflag; str += ch; if (nflag >= 4) //進入爬取單詞尾部狀態 state = 3; } else { //不知足單詞開頭有4個字母 state = 1; } break; case 3: //爬取單詞尾部狀態 if (isdigit(ch) || isalpha(ch)) { str += ch; } else { //處理和存入字符串 transform(str.begin(), str.end(), str.begin(), ::tolower); state = 0; wordCount++; map[str]++; } break; default: break; } } if (state == 3) //處理最後階段3跳出沒來的及處理的字符串 { transform(str.begin(), str.end(), str.begin(), ::tolower); wordCount++; map[str]++; }
//遍歷找出頻率最高的top10單詞 for (auto iter = map.begin(); iter != map.end(); iter++) { if (isWordGreater(iter->first, iter->second, tempArr[9].word, tempArr[9].count)) { tempArr[9].word = iter->first; tempArr[9].count = iter->second; for (int i = 9; i >= 1; i--) { if (isWordGreater(tempArr[i].word, tempArr[i].count, tempArr[i - 1].word, tempArr[i - 1].count)) { swap(tempArr[i], tempArr[i - 1]); } } } } fin.close(); return wordCount; }
//主函數 int main(int argc, char *argv[]) { TextAnalysisUtil TA; ifstream infile; WordInf tempArr[10] = { }; infile.open(argv[1], ifstream::binary | ifstream::in); if (!infile) { cout << "失敗" << endl; return 1; } infile.close(); ofstream outFile; outFile.open("result.txt"); if (!outFile) { cout << "失敗" << endl; return 1; } outFile << "characters: " << TA.asciiCount(argv[1]) << endl; outFile << "words: " << TA.wordAnalysis(argv[1],tempArr) << endl; outFile << "lines: " << TA.countLines(argv[1]) << endl; for (int d = 0; d < 10; d++) { if(tempArr[d].count>0) outFile <<"<"<<tempArr[d].word << ">: " << tempArr[d].count << endl; } }
程序改進的思路:
花費最長時間的函數毫無疑問的是爬取單詞並排序的wordAnalysis。對於此函數的優化咱們在設計之初便盡本身目前的知識所能進行優化。
首先字典是毫無疑問使用的map類型的容器。
---------------------
map: map內部實現了一個紅黑樹,該結構具備自動排序的功能,所以map內部的全部元素都是有序的,紅黑樹的每個節點都表明着map的一個元素,所以,對於map進行的查找,刪除,添加等一系列的操做都至關因而對紅黑樹進行這樣的操做,故紅黑樹的效率決定了map的效率。
unordered_map: unordered_map內部實現了一個哈希表,所以其元素的排列順序是雜亂的,無序的
---------------------
經過查閱資料咱們選擇了unordered_map,由於在爬取單詞時並不須要隨時更新排序,而最終目標是選出top10,因此咱們考慮得出在爬取完成後不採起整個排序而是,採起遍歷原數據,並新設一個10大小結構數組,本身設計比較大小的函數,對每一個新遍歷元素,進行在10大小結構數組冒泡排序,這樣平均複雜度是o(10n)。工具
部分代碼
TEST_METHOD(TestMethod1) { TextAnalysisUtil TA; char temp[] = "D:\\搜狗高速下載\\測試樣例\\input2.txt"; int a = TA.asciiCount(temp); Assert::AreEqual(76, a); // TODO: 在此輸入測試代碼 } TEST_METHOD(TestMethod2) { TextAnalysisUtil TA; char temp[] = "D:\\搜狗高速下載\\測試樣例\\input2.txt"; int a = TA.countLines(temp); Assert::AreEqual(3, a); // TODO: 在此輸入測試代碼 } TEST_METHOD(TestMethod3) { TextAnalysisUtil TA; char temp[] = "D:\\搜狗高速下載\\測試樣例\\input2.txt"; WordInf Wtemp[10]; int a = TA.wordAnalysis(temp, Wtemp); Assert::AreEqual(strcmp(Wtemp[0].word.c_str(), "abcdefghijklmnopqrstuvwxyz"), 0); Assert::AreEqual(Wtemp[0].count, 1); Assert::AreEqual(Wtemp[1].count, 0); Assert::AreEqual(a, 1); }
Input2.txt(測試樣例)
windows95
windows98
windows2000
windows98
windows2000
windows2000 windows98 windows98
windows2000
windows2000
windows98
windows98
a34
aa34
aaa34
aaaa34
1234
1aaaa4
aaaa
{1fafad
Aaaa
解釋:
因爲爬取單詞代碼是依照狀態機思路完成的,測試數據主要是各種型字符串(開頭爲字母或者數字或者非字母數字,各類類型的中間過程,末尾的過濾)的爬取是否有沒有問題。以及檢測字典排序是否有問題。
1.一開始不知道從哪裏開始下手。就對需求進行了細讀分析,發現需求也分紅了較大的版塊。因而就對大的板塊着手進行分析。調出關鍵部分函數進行分析構建,也就有了入手之處。
2.因爲沒找到適合儲存單詞的類,就構造了一個類來存儲,解決了單詞存儲問題。
3.對分隔符一開始理解不夠,覺得就是空格。後來才發現分隔符在需求上有定義。因此認真看需求也很重要。
4.對於成爲單詞的條件沒有徹底使用正則表達式來實現,而是使用了標誌位加正則表達式實現,想來是複雜了。
5.有一些細節問題,開始編碼的時候未發現,後來經過步入調試一一解決了。
雖然只完成了基礎需求部分。但從需求到實現,也動了不少腦筋。好比字符的讀入,正則表達式的讀入,數據的存放,排序和命令行參數。中間的調試也花了很多時間。最後測試完,也對代碼進行了一些調整。總之,就是不會就上網搜索學習,有好的方法也要跟着吸取。
本身:完成了部分代碼的編寫和調試,編程能力獲得了提高,同時也完成了測試和代碼嵌入的部份內容,也認識到本身的能力上的不足又學到複習了不少實用的知識。進階部分沒來得及完成仍是很遺憾的。
隊友:認真負責,積極提供了重要的幫助,也完成了另外一部分的工做,合做很愉快。
此次任務讓咱們的隊伍接觸到了並瞭解到了外界具體需求究竟是什麼樣子,爲之後作了微小的一點點準備。
同時也讓咱們複習到了之前的知識,培養了動手能力,很合理。
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
• Estimate | • 估計這個任務須要多少時間 | 40 | 20 |
Development | 開發 | ||
• Analysis | • 需求分析 (包括學習新技術) | 300 | 240 |
• Design Spec | • 生成設計文檔 | 30 | 30 |
• Design Review | • 設計複審 | 10 | 20 |
• Coding Standard | • 代碼規範 (爲目前的開發制定合適的規範) | 10 | 10 |
• Design | • 具體設計 | 200 | 350 |
• Coding | • 具體編碼 | 500 | 300 |
• Code Review | • 代碼複審 | 120 | 100 |
• Test | • 測試(自我測試,修改代碼,提交修改) | 500 | 540 |
Reporting | 報告 | 30 | 50 |
• Test Report | • 測試報告 | 30 | 20 |
• Size Measurement | • 計算工做量 | 30 | 20 |
• Postmortem & Process Improvement Plan | • 過後總結, 並提出過程改進計劃 | 20 | 15 |
合計 | 1820 | 1715 |