1.WordCount項目:031602510/scrc++
2.psp表格git
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 80 | 60 |
· Estimate | · 估計這個任務須要多少時間 | 80 | 60 |
Development | 開發 | 2000 | 2700 |
· Analysis | · 需求分析 (包括學習新技術) | 400 | 450 |
· Design Spec | · 生成設計文檔 | 30 | 50 |
· Design Review | · 設計複審 | 30 | 60 |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 60 | 60 |
· Design | · 具體設計 | 150 | 180 |
· Coding | · 具體編碼 | 1000 | 1300 |
· Code Review | · 代碼複審 | 60 | 60 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 280 | 370 |
Reporting | 報告 | 90 | 120 |
· Test Repor | · 測試報告 | 60 | 90 |
· Size Measurement | · 計算工做量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 實際用的時間會比起估計時間長上許多 | 因此應該安排好時間提早幾天完成任務,否則會形成最後幾天瘋狂趕代碼的狀況 |
3.解題思路描述。即剛開始拿到題目後,如何思考,如何找資料的過程。 |
解題思路:仔細閱讀做業題目以後,發現程序要完成四件任務:github
估摸着應該是能夠用函數fopen()來打開文件,而後用 fgetc()來逐個獲取文件中的字符,這個應該不難實現,不過寫出來的代碼才發現提示fopen()是unsafe的,查了資料才知道如今都建議用函數fopen_s(),兩者在接受參數和返回值上都有區別。算法
<2>統計文件的單詞總數,單詞:<span style ="color:red">至少以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。</span>windows
一開始沒有看清楚後面的單詞實例,一開始感受要求有歧義,走了不少彎路。對於單詞總數的統計,個人思路是先將輸入的<span style ="color:blue">input.txt</span>文件存入一個字符串變量之中,而後對字符串進行預處理———大寫轉化爲小寫,除英文字母,數字之外的字符統一用空格符號代替,而後將字符串按空格隔開放進<span style ="color:blue">unordered_map<string, int> strMap;</span>中,最後用容器vector和定義的sort();生成字典排序,而後遍歷將符合條件的單詞個數累加。網絡
行數的統計雖然會稍微簡單點,不過也有一些坑點,好比空白行可能存在着空格不僅有<span style ="color:blue">"\n"</span>,我寫的程序一開始嗨啊忽略了第一行是空白的狀況,屢次測試以後才發現問題;app
<4>統計文件中各單詞的出現次數,最終只輸出頻率最高的10個。頻率相同的單詞,優先輸出字典序靠前的單詞。函數
一開始把result.txt文件忘了,後來通過舍友提醒才知道的,關於單詞頻率的統計思路和以前寫的單詞數統計的主要思想差很少,不過統計的是字典序的單詞,不過今天思考了一下比起用sort()函數,用堆排序單詞會更快,還有據說有的同窗遍歷了十次容器每次取出最小的字典序單詞,只須要O(10N),能夠說是很黑科技了。工具
4.設計實現過程。設計包括代碼如何組織,好比會有幾個類,幾個函數,他們之間關係如何,關鍵函數是否須要畫出流程圖?單元測試是怎麼設計的?性能
設計的實現過程: 類圖
流程圖
單元測試的設計, 其實總共有十個測試的,就不一一列出來了
text.cpp #include "stdafx.h" #include "CppUnitTest.h" #include "../WordCount/stdafx.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace UnitTest1 { TEST_CLASS(UnitTest1) { public: TEST_METHOD(TestMethod1) { int Charnum = 198;//指望的測試結果 int WordNum = 7; int lineNum = 3; char* filename = "F:\\WordCount\\UnitTest1\\test.txt";//文件存放位置絕對路徑 FILE *fp; fopen_s(&fp, filename, "r"); WordCount A(fp, filename); Assert::AreEqual(lineNum, A.LineCount());//用的函數接口做爲參數作測試 Assert::AreEqual(Charnum, A.CharCount()); Assert::AreEqual(WordNum , A.WordNum()); } }; }
測試結果查看
5.記錄在改進程序性能上所花費的時間,描述你改進的思路,並展現一張性能分析圖(由VS2017的性能分析工具自動生成),並展現你程序中消耗最大的函數。
cpu佔用圖
main函數佔用
佔用最大的操做原來和vector有關
代碼覆蓋率
改進思路,對於容器vector和排序sort()是能夠找到更好的替代,用堆排序或者遍歷10次Map應該會減小算法複雜度。
6.代碼說明。展現出項目關鍵代碼,並解釋思路與註釋說明。
下面就是本身以爲花了比較多時間的核心代碼:主要是單詞以及頻率輸出部分,其餘就不作贅述。
unordered_map<string, int> strMap; bool mysort(pair < int, string > a, pair < int, string > b)//定義sort規則 { if (a.first != b.first) return a.first > b.first; else return a.second < b.second; } void GetMap(stringstream &ss)//生成string中單詞的鍵值對 { string strTmp; while (ss >> strTmp) { unordered_map<string, int>::iterator it = strMap.find(strTmp); if (it == strMap.end()) { strMap.insert(unordered_map<string, int>::value_type(strTmp, 1)); } else strMap[strTmp]++; } } void WordCount::LetterCount() //字符統計函數 { string strFile, tmp; int i = 0; ifstream file(target_file); //讀取當前文件夾下input.txt文件 while (getline(file, tmp))//直到文件結尾,依次逐行讀入文本 { strFile.append(tmp); //每次讀入一行附加到strFile結尾 strFile.append(" ");//行尾補充空格 tmp.clear(); //記得清除,不然上一次讀入比此次文本長,不會徹底覆蓋而出錯 } for (unsigned int i = 1; i <= strFile.size(); i++)//將字符串中英文字母大寫轉小寫 { if (strFile[i] >= 'A'&&strFile[i] <= 'Z') strFile[i] += 32; } for (unsigned int i = 0; i < strFile.length(); i++)//分隔符號替換成爲空格 { if (ispunct(strFile[i])) strFile[i] = ' '; } stringstream ss(strFile); if (strMap.empty()) GetMap(ss); vector < pair < int, string > > m;//容器 for (unordered_map<string, int>::iterator it = strMap.begin(); it != strMap.end(); ++it) m.push_back(make_pair(it->second, it->first)); sort(m.begin(), m.end(), mysort);//排序 for (unsigned int k = 0; k < m.size(); ++k)//輸出單詞以及頻率 { string a = m[k].second.c_str(); if ( ((a[0] >= 'a'&&a[0] <= 'z') || (a[0] >= 'A'&&a[0] <= 'Z')) && ((a[1] >= 'a'&&a[1] <= 'z') || (a[1] >= 'A'&&a[1] <= 'Z')) && ((a[2] >= 'a'&&a[2] <= 'z') || (a[2] >= 'A'&&a[2] <= 'Z')) && ((a[3] >= 'a'&&a[3] <= 'z') || (a[3] >= 'A'&&a[3] <= 'Z'))) { cout << '<' << a << '>' << ":" << m[k].first << endl;//打印結果 i++; if (i >= 10)//輸出頻率前十 break; } } }
7.結合在構建之法中學習到的相關內容與我的項目的實踐經歷,撰寫解決項目的心路歷程與收穫。
一開始並無感受第二次做業會很難,git的操做以前有過,都蠻順利的,寫代碼也沒有花費不少時間。看到題目以後捋了一下思路,其實題目就是在以前c/c++課程的基礎上,把一個個簡單的功能糅合成一個程序,往裏面摻雜一些文件的讀寫,排序,字符串處理等一些「簡單」操做,應該不難。
可是當調試的時候,和同窗一對比運行結果,就發現很多問題,代碼有不少bug致使結果出錯,主要仍是沒有考慮周全,有的地方代碼不得不從新寫過,用到Map和容器的時候,因爲不熟悉,甚至結果徹底出錯,在電腦面前找了一個多小時,最後發現是本身寫的Get Map()(往Map裏面傳數據的函數)屢次傳入,致使單詞頻率的統計結果翻了幾倍。
接下來就是單元測試?什麼是單元測試??怎麼測試???這是第一次聽的時候的心理活動,不過萬能的百度和藹良的同窗給了我很多幫助,最後也學會了編寫測試代碼,看測試結果是否正確。期間還有一個小插曲,寫好測試程序以後無論怎麼測試,都會有部分經過不了,可是運行結果是沒錯的?!本身折騰了一兩個小時,查Assert::AreEqual()的不少資料,感受測試結果不該該不對啊,而後舍友說,你再運行一下未經過的測試,而後竟然能經過了。
最後對於封裝,也是隻知其一;不知其二,感受封裝的也不是很完善,第一次認真正式地封裝。
如今對於此次的做業,感受就是各類觸及個人知識盲區,不過有所得。說實話,和周圍同窗比起來,做業對於我來講是挺吃力的,甚至於過程是艱難的,不過能聽聽同窗聊聊程序的改進,向他們學習,上網絡上查資料,本身也把查資料過程當中的好博客和內容摘抄下來,積累起來,也有欣慰。