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()的不少资料,感受测试结果不该该不对啊,而后舍友说,你再运行一下未经过的测试,而后竟然能经过了。
最后对于封装,也是只知其一;不知其二,感受封装的也不是很完善,第一次认真正式地封装。
如今对于此次的做业,感受就是各类触及个人知识盲区,不过有所得。说实话,和周围同窗比起来,做业对于我来讲是挺吃力的,甚至于过程是艰难的,不过能听听同窗聊聊程序的改进,向他们学习,上网络上查资料,本身也把查资料过程当中的好博客和内容摘抄下来,积累起来,也有欣慰。