• 课程名称:软件工程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 |