本次大做業,咱們小組完成基本任務,擴展任務以及高級任務。下面是每一個任務的說明。git
一、首先給出GitHub地址:https://github.com/DM-Star/WordCount-optgithub
二、PSP表格性能優化
PSP2.1框架 |
PSP階段編輯器 |
預估耗時ide (分鐘)函數 |
實際耗時工具 (分鐘)性能 |
Planning單元測試 |
計劃 |
10 | 15 |
· Estimate |
· 估計這個任務須要多少時間 |
10 | 15 |
Development |
開發 |
700 | 800 |
· Analysis |
· 需求分析 (包括學習新技術) |
80 | 90 |
· Design Spec |
· 生成設計文檔 |
30 | 50 |
· Design Review |
· 設計複審 (和同事審覈設計文檔) |
60 | 80 |
· Coding Standard |
· 代碼規範 (爲目前的開發制定合適的規範) |
20 | 30 |
· Design |
· 具體設計 |
50 | 50 |
· Coding |
· 具體編碼 |
300 | 400 |
· Code Review |
· 代碼複審 |
40 | 50 |
· Test |
· 測試(自我測試,修改代碼,提交修改) |
30 | 50 |
Reporting |
報告 |
100 | 80 |
· Test Report |
· 測試報告 |
50 | 40 |
· Size Measurement |
· 計算工做量 |
30 | 20 |
· Postmortem & Process Improvement Plan |
· 過後總結, 並提出過程改進計劃 |
30 | 30 |
三、主要代碼與接口說明
在這次大做業中,咱們組將整個任務分紅4個模塊,分別是核心模塊、統計模塊、狀態模塊和輸入模塊,和老師要求中說的有一點不一樣,但咱們一塊兒討論以爲咱們這個項目分紅這樣四個模塊更加合理,我負責的是核心模塊,核心模塊主要是主函數和輸出函數這兩個部分(在這裏也先提一下,就是這兩個恰好用單元測試框架就很難實現,研究百度很久也不清楚怎麼測main函數和代碼中的output函數,因而沒辦法和其餘模塊同樣用單元測試框架),下面給出部分代碼和接口的解釋。
①main函數主要代碼
int main(int argc, char **argv) { fstream in; if (!inputCheck(argv[1], in)) { in.close(); return 0; } WordList wordList; wordCount(in, wordList); outPut("result.txt", wordList); in.close(); system("pause"); return 0; }
這個主函數裏主要就是包含了你們寫的不一樣模塊與接口,inputCheck是檢查輸入函數,WordList是爲了記錄單詞及其詞頻的一個類,outPut就是輸出函數,還有wordCount函數是對文件裏面的單詞進行詞頻統計和排序。首先用fstream in定義了一個文件對象(它封裝了各類操做文件的方法),在接下來的函數中做爲參數傳入,在對文件輸入判斷檢測後,對單詞進行遍歷統計,最後輸出結果在result.txt文件上。
②outPut函數部分代碼
void outPut(char outFile[], WordList &wordList) { ofstream outf(outFile); streambuf *default_buf = cout.rdbuf(); cout.rdbuf(outf.rdbuf()); wordList.outPut(); cout.rdbuf(default_buf); }
這是主運行函數中的輸出代碼,前三行輸出重定向到文件,最後一行輸出重定向到屏幕,中間一行負責用cout輸出。
void WordList::outPut() { // 100 words are all output via cout Word *p = pWordHead->next; if (p != pWordTail) cout << p->word << ' ' << p->num; else return; p = p->next; for (int i = 0; i < 99; i++) { if (p != pWordTail) cout << endl << p->word << ' ' << p->num; else return; p = p->next; } }
這裏是wordlist類中的一個輸出函數,用於輸出文件中的具體內容,根據其餘組員設計的鏈表,這裏的每一個節點p是一個結構體,裏面定義的char word和int num,我這裏用p->word和p->num表示。這段代碼首先,定義一個指針指向從表頭以後的第一個節點(這纔是正文的內容開始),判斷是不是表的尾部,若是不是,就輸出內容,即單詞和單詞數;而後p指向下一個節點,全部的輸出用一個for循環,根據老師要求,輸出詞頻前100位的單詞。
四、測試用例設計
在前面也提到了,在我提出main函數該怎麼測試這個問題後,咱們你們都思考的狀況下,都以爲不知道怎麼用框架去測試,後來組長也上去問了老師,老師的回答也是跟咱們一致的。後來我想,主函數和輸出函數要測試的話,其實就是測試整個做業的輸入輸出了,因而我就設計了20個測試用例,至關於測試了咱們這整個做業的功能是否徹底完成,是否在遇到什麼特殊狀況是都能正確輸出結果,以及這個項目的性能。
我設計測試用例的思想及整個過程以下:
首先對整個項目進行一個整體的測試,判斷是否能正常輸出想要的結果,以下圖,與預期相同
而後老師任務要求中寫只對txt文件進行分析,那對於其餘格式的文件確定是沒辦法解析的,因此這裏我選擇的幾種比較典型的文件格式試運行,例如.cpp文件、.h文件、word文件等,結果與預期同樣,如圖
而後就是測試的重點,功能測試,測試咱們的代碼是否能徹底正確的運行想要的結果,在這,我設計的十幾個測試用例,分別檢測了特殊狀況,如老師例子中給出的
有關單詞識別的部分典型狀況的說明:
第一,Let’s,這種包含單引號的狀況,視爲2個單詞,即let和s。
第二,night-,帶短橫線的單詞,視爲1個單詞,即night。
第三,「I,帶雙引號的單詞,視爲1個單詞,即i。
第四,TABLE1-2,帶數字的單詞,視爲1個單詞,即table。
第五,(see Box 3–2).8885d_c01_016,帶數字、經常使用字符和單詞的狀況,視爲4個單詞,即see, box, d, c。
前四種狀況運行的蠻順利,截圖
在最後一種狀況時,發現的一處問題,運行結果跟預期不一樣,以下圖,c單詞明明只出現了一次,詞頻確實2。後來發現應該這個測試用例中「3–2」中的擴展字符,它都會讀成單詞c。
這個問題引起我又有了一個新的測試用例,即更明確驗證一下是否是那個「–」引發的問題,因而我在上面的測試用例後面又加了好幾個「–」,結果代表猜測是正確的,
而後我將以上測試放在同一個文件裏,每種數量也翻倍,來進行綜合測試,輸出結果與預期相同。
而後還進行了對空文件的測試,若是文件爲空,看會輸出什麼結果,運行如圖
最後的測試就進行了一個簡單的性能測試,特地製造了一個1M以上的大文件進行測試,經過對比原文件與不停複製粘貼後的大文件輸出結果,發現結果無誤,即也經過了小小的性能的測試。(具體的壓力測試還在高級任務中)
綜上,除了上面「–」引發的問題,其餘全部測試用例輸出都與預期相同,即完成了測試任務。
測試用例清單表以下圖:
使用靜態測試是爲了確保代碼符合行業規範。在這個項目中,咱們參考了Google給出的C++風格指南,而且對全部的代碼進行了檢查。Google給出的代碼規範涉及的範圍十分全面,從頭文件、命名空間,一直到if……else……語句,到註釋、空格、花括號,都給出了詳盡的規範。咱們原本想使用Google提供的全部規範,但後來失敗了。一方面,有些規範咱們不是很能理解,例如Google對於命名空間提出的規範,咱們並不能徹底地理解;另外一方面,規範中的某些內容咱們並不承認,好比對於變量的命名,Google推薦使用下劃線分隔變量中的每一個單詞,而咱們認爲變量命名使用變量首字母小寫、單詞首字母大寫,中間不使用空格分隔的方式也很好(並且這是面向對象程序設計老師推薦的命名方式)。
綜上,最後咱們僅使用了頭文件、註釋、格式上的規範對整個項目進行代碼檢查。
進行代碼檢查,咱們使用的是一款也是由Google提供的代碼檢查工具cpplint(下載地址爲https://github.com/google/styleguide/tree/gh-pages/cpplint)。這款工具十分方便,對於一款配置好Python環境的電腦來講,只要將被測文件和腳本文件cpplint.py放在同一目錄下,而後使用控制檯運行Python腳本(被測文件路徑做爲參數),就可以快速地進行測試。
下面進行的第一次測試:
能夠看出咱們的代碼主要存在的問題就是格式,以及一點點頭文件的問題。Google規定代碼中全部用Tab製表符的地方都得使用空格,每一個花括號以前都要有一個空格,以及對於if……else……語句來講,只要一個分支使用了花括號,那麼全部的分支都要使用花括號,並且else分支必須和先後兩個花括號處在同一行。對於註釋,註釋內容和雙斜槓之間必需要留有一個空格(我也不知道爲何)。
// 符合Google 規範的代碼 if ((c >= 'a') && (c <= 'z')) { state = INNERWORD; } else if (c == '-') { if (state == INNERWORD) state = CRITICAL; else state = OUTERWORD; } else { state = OUTERWORD; }
對於頭文件而言,每一個頭文件都必須給出版權聲明,還要進行#define保護。
// Copyright[2018]<Star> #ifndef WCPRO_WORDLIST_H_ #define WCPRO_WORDLIST_H_ // 在這裏添加代碼... #endif // WCPRO_WORDLIST_H_
不得不說,Google給出的規範真的是面面俱到。然而真的要實現它的全部規範我也不是很願意。就拿#define保護來講吧,這三行代碼,在VS編譯器下,只須要用 #pragma once 這一條指令就能夠實現。對於if……else……分支的種種規範,我以爲都不少餘。我我的自己也有一套本身的格式規範,個人if語句看起來原本也至關順眼,很是整齊,改爲Google規範後反而看着不舒服了。就咱們所採用的那部分Google規範而言,咱們實際上都是差很少的。緣由就是,在VS編輯器中,調整代碼格式能夠很方便地使用一組組合鍵來完成(Ctrl+K 和 Ctrl+F),使用這個組合鍵,能夠快速地將咱們的代碼調整至VS的代碼規範。對於沒有采用Google規範的那部分(即,變量命名),我自認爲勝他一籌,我對於變量命名,都會確實使用變量的含義命名,如pIndex, wordState等等,不會使用形如c, p, q 這樣的簡單的單字母來命名。
靜態測試上面這部分的內容引自咱們組長的博客,由於這個靜態檢查是從我開始作的,而開始檢查的時候,他也一塊兒正旁邊看,而後一塊兒討論一塊兒改正,因此咱們想法一致,尤爲是那個if……else……分支的種種規範,咱們改了好久,才改爲了符合要求的規範。
由於開始檢查的時候就是隨便選了一個文件檢查,截圖的代碼不是本身寫的那部分,後面就沒有截圖,由於問題和改正方法都類似。
本次做業在個人mian函數和輸出函數中性能優化中體現不大,可是在別的組員如輸入模塊上,對文件讀取方面性能優化就比較須要,在讀一個很大的文件時須要耗費的時間上就有體現。
本次大做業,相比我的做業,感受學習到的東西更多了,由於此次做業涉及到的知識面也多,學到了框架測試,靜態測試,還有各類規範等等。第一次接觸靜態測試工具,跑出來結果的時候感受都驚呆了,居然幾乎每行代碼都是不符合規範的,因而一行一行的改,感受這個工具也是蠻神奇的。還有,一個團隊做業時,不一樣的同窗都有不一樣的想法,互相交流討論,感悟也不少。最後,要謝謝組長,帶領咱們學習,圓滿完成此次大做業!