英文文本词频分析系统设计报告
组员:李少楠、张书桓
0 前期工作
团队分工:
组长:李少楠
主要工作:领导项目,负责软件的交付工作以及编写程序
优点: 1.擅长后台程序开发
2.擅长系统设计
3.有较强解决bug问题的能力
组员:张书恒
主要工作:收集资料,NABCD,帮助提供单元测试的具体内容
优点: 1.擅长交流与分析
2.对实际问题有较强的分析解决能力
结对编程回顾:
在结对之前,我们组体现的就是重视代码的设计,我们讨论了如何做才能使代码有良好的扩展性和易读性,在这一方面丝毫不吝啬讨论的时间。我也越来越感受到写优雅的代码比写大量的毫无设计的代码更有趣。
体会到的结对的优势:
- 相互督促,提高代码质量
结对时一个人编码,一个人做审查工作。这样我们更能集中精力,当发现路线走偏了,能立即纠正。或是做一些代码规范,代码效率的提醒。
- 相互学习经验
在观察队友的同时,可以学习对方编码的经验。就我而言,除了观察队友的编码思路意外,我还会学习他是如何使用IDE的,他用到的IDE的功能,哪些是我没有掌握的,甚至是他在开发过程中用到的其他我没有接触过的工具,这些都是值得学习的。
结对的问题:
1. 需要磨合的时间
2. 沟通也需要大量的时间
最后还要感谢我的组长少楠,在软件设计方面以及项目的调试都教会了我很多,和他一起合作很愉快!
1 代码仓库中项目地址:
https://gitee.com/li_shao_nan/SoftwareEngineeringDevelop.git
2 PSP表格
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
|
|
Estimate |
估计开发时间 |
120 |
80 |
Development |
开发 |
|
|
Analysis |
需求分析 |
60 |
100 |
DesignSpec |
生成设计文档 |
40 |
50 |
DesignReview |
设计复审 |
20 |
30 |
CodingStandard |
代码规范 |
60 |
40 |
Design |
具体设计 |
240 |
180 |
Coding |
具体编码 |
420 |
540 |
CodeReview |
代码复审 |
120 |
30 |
Test |
测试(自我测试,修改代码,提交修改) |
120 |
120 |
ReportingTimeSpent |
报告 |
60 |
80 |
TestRepor |
测试报告 |
60 |
30 |
SizeMeasurement |
计算工作量 |
60 |
30 |
Postmortem |
事后总结 |
30 |
40 |
ProcessImprovementPlan |
提出过程改进计划 |
30 |
20 |
|
合计 |
1440 |
1380 |
3 每日软件工程学习日志
学习时段 |
学习内容 |
收获体会 |
自我效率评价 |
2019/11/01 20:00——21:00 |
个人能力的衡量与发展; 软件需求 |
通过对自身能力的评估,寻找到比较合适的项目方向,并通过讨论明确了项目的基本的必要需求 |
在一个半小时内,我们明确了要做一个英文文本单词分析软件,并且通过查资料获取了此类软件的基本功能,并进行了需求分析,方向更改的决定做的比较果断,效率较高 |
2019/11/02 10:00——12:00 |
结对编程; 竞争性需求分析 |
通过对用户需求和竞争性需求两方面的考虑,完善了项目的必要需求和提出若干个杀手功能 |
由于没有时间进行调查问卷、和文献资料搜索,只能结对讨论,通过自己对用户需求与其他软件功能的了解,进一步完善项目的功能,效率较低 |
2019/11/03 9:00 —— 2019/11/04 24:00 |
英文文本单词分析软件的程序构建 |
对文件的读取与写入、c++ STL、google编程规范有了进一步的了解; 成功实现了预先设定的部分必要需求和杀手功能 |
通过两天的编程,灵活地参考CSDN上的高亮代码、对STL的使用还不够熟练、编程过程中没有百分百符合google编程规范,后续需要进一步规范代码编写与程序结构构建,效率中等 |
2019/11/05 8:00——10:00 |
英文文本单词分析软件的程序的结构优化与代码规范化 |
实现了搜索功能;通过重整程序的结构,增强了程序的易读性与可修改性,变量名设定更加人性化 |
重新分析自己写的代码是一件狠痛苦的事情,所以通过队友的角度看问题更容易发现自己的盲点,效率中等;后续需要提前建立起程序结构的框架以及进一步优化数据结构,分析大文本(>5M)时,程序较慢(30s)。 |
2019/11/05 10:00——14:00 |
完善设计文档、报告、以及总结 |
每天都记录自己的工作是一件很棒的事情,知道自己在何时何刻的想法;记录达成的成就和待完成的事项有助于时间的规划 |
由于时间安排不合理,导致单元测试没有来得及编写,只能通过手动测试,十分费时费力,后续应一边构建程序一边编写单元测试 |
4 解题思路描述
最开始我们的题目是想对“别踩白块”(或“节奏大师”)这个游戏进行改进,增加一些功能使其能够具有一定的背单词和支持用户自定义背景音乐等功能,但是由于近期学习压力较大、且每天都有集体任务,时间比较紧凑,没有那么多的时间进行收集资料和学习之前一点都不了解的技术。所以经过结对讨论与分析,综合我们的个人能力和空闲时间总量,最终决定设计一个英文文本单词分析软件。我们通过上网搜索相关软件的博客、查看优质的代码片段、对比不同软件所实现的需求,得到我们的竞争性需求分析。
4.1 Need:
必要要求:实现词频统计、单词查找定位
辅助功能:将词频表按词频大小排列、按字母表顺序排列
杀手功能:将词频表中具有相同常见前缀、后缀的单词归类并输出;建立用户词库:可存、删某单词的近义词、反义词
4.2 Approach:
4.2.1 在word中对英文文本进行预处理,去除全角标点符号。
4.2.2 生成无排序列表
4.2.3 生成按词频排序、按字母表排序词频列表
4.2.4 建立Key:后缀——Value:单词的multimap图
4.2.5 实现单词检索功能
4.3 Benefit:
4.3.1 提供按频次、按字母表顺序输出到指定文本,可获取某一单词出现的频次,便于用户进行有针对性地单词查询,提高用户的文本阅读效率;
4.3.2 提供单词定位功能: 提供用户查看该单词第一次出现的段落及位置的功能,便于用户建立单词词义与所在文本环境的联系,增强用户对单词词义的理解;
4.3.3 针对性地建立Key:后缀——Value:单词的multimap图,检索出所有具有相同前缀或后缀的单词,并输出到指定文本,便于用户对具有相同前缀或后缀单词的记忆与学习;
4.3.4 (待完成)可建立用户的个人词库,存储单词及其词义,通过重载运算符实现“单词——同义词(或)反义词”配对、并输入
4.3.5 (待完成)进行网页编程、实现输出英文文本到网页上,通过鼠标左右键点击,实现查询、添加单词词义,查询词频、建立同义词(或反义词)的配对、高亮文本中的所有该单词。
4.4 Competition(C):通过上网搜索,其他词频计算软件均没有实现后缀、前缀、以及单词查询定位功能,这将成为我们的优势;后续通过网页编程,或者建立更加良好的用户界面,将进一步提高我们软件的竞争力。
4.5 Deliver(D):
4.5.1 将程序或者网页链接发到英语课程学习微信群
4.5.2 以插件的形式附加在图书馆的电子检索页面上
4.5.3 以广告的形式附加在英文txt文档下载的网页上
4.5.4 推广到手机应用中或者发布到苹果AppStore
5 设计实现过程及代码说明
5.1 WordElement 结构体:存储单词的内容、词频及位置
typedef std::string Word; typedef int Freq; typedef int ParagraphNum; typedef int LocationInParagraph; struct WordElement { Word word_content; // 单词 Freq word_frequency; // 词频 ParagraphNum word_para_num; // 单词第一次出现的段落数(因为getline以回车符结束) LocationInParagraph word_location; // 单词第一次在段落中出现的位置 };
5.2 WordAnalysisInArticle类:实现读取文本、输出相应词频列表到指定文本、生成“后缀——单词” multimap、实现搜索功能
class WordAnalysisInArticle { public: // 读取英文文本,将词频存入无排序列表 word_unsorted void read_file(FileName & kFilename_1); // 按内容、按词频 排序 void sort_by_content(); void sort_by_frequency(); // 将排序后的词频表写进文本 void write_word_sorted_by_content(FileName &kFilename_2); void write_word_sorted_by_freq(FileName &kFilename_3); // 将具有相同后缀的 word 写入 word_suffix_map void add_word_suffix_map(const Word &word); // 从 unsorted_word 建立 word_suffix_map void build_map_from_word_unsorted(); // 将 word_suffix_map 写入 文件中 void write_map_file(FileName &kFilename_4); // 搜索函数 // 搜索 word 在文本中第一次出现的段落及位置 bool search_word(const Word &word); // 析构函数 ~WordAnalysisInArticle(); private: std::vector<WordElement> word_unsorted; // 单词列表 std::vector<WordElement> word_sorted_by_freq; // 词频排序表 std::vector<WordElement> word_sorted_by_content; // 词频排序表 // 常见后缀 Suffix SuffixVector suffix_vector = { "ment", "ance", "bility", "hood", "sion", "tion", "ism", "ness", "tude", "ble", "ive", "ish", "like", "ful", "ous", "less", "ent", "ize", "ise", "most"}; // multimap : Key:后缀(Suffix) ;Value:单词 (word) WordSuffixMap word_suffix_map; };
5.3 定义功能单词(常见的代词、虚词、副词等std::unordered_multiset<std::string> function_word
{ "the", "a", "an", "other", "another", "is", "are", "am", "was", "were", "been", "be", "i", "you", "he", "she", "they", "it", "all", "one", "me", "him", "her", "them", "we", "this", "those", "these", "our", "yourself", "yourselves", "my", "his", "hers", "their", "for", "as", "of", "go", "what", "where", "who", "when", "how", "which", "that", "will", "its", "why", "whatever", "wherever", "whoever", "whenever", "however", "but", "ever", "and", "at", "in", "on", "by", "up", "down", "about", "along", "upon", "through", "to", "isnt", "do", "doesnt", "dont","did", "wasnt", "werent", "im", "youre", "with", "toward", "has", "have", "off", "from","had", "gone", "any", "some", "just", "so", "then", "here", "there", "out", "off", "ok", "until", "end", "begin", "almost", "always", "often", "sometimes", "good", "say", "said", "went", "hello", "some", "any", "than", "now", "today", "says", "make", "take","okay", "not", "no", "if", "into", "would", "never", "too", "us", "may", "maybe", "might", "can", "should", "shall", "could", "under", "beyond", "though", "more", "much", "many", "such", "yes", "man", "men", "woman", "women", "also", "himself", "herself", "ourselves", "themselves" };
5.4 两个静态函数作为功能函数
// 判断是否具有相同后缀 Suffix static bool WithSameSuffix(const Word &word, const Suffix &suffix); // 去掉标点符号及转小写字母 static std::string cleanup_str(const Word&);
5.5 两个比较函数、用来输出排序后的词频表
// 按词频大小比较 bool comp_frequency(const WordElement &word_element_1, const WordElement &word_element_2); // 按字母表顺序比较 bool comp_word(const WordElement &word_element_1, const WordElement &word_element_2)
5.7 主函数及程序结果:
#include <iostream> #include <vector> #include <fstream> #include <string> #include <string.h> #include <iomanip> #include <sstream> #include <unordered_set> #include "WordElement.h" #include "FunctionWord.h" #include "WordAnalysisInArticle.h" int main() { WordAnalysisInArticle word_freq_calculate; // 处理英文文本 word_freq_calculate.read_file("A_Game_Of_Thrones_1.txt"); // word_freq_calculate.search_word("dark"); // 输出到 WordSortedbyContent.txt std::string str_word = "WordSortedbyContent.txt"; std::ofstream ofs_word(str_word.c_str()); // 清除待写入文本内容 if (ofs_word.good()) { remove(str_word.c_str()); } word_freq_calculate.write_word_sorted_by_content(str_word); // 输出到 WordSortedbyFreqVector.txt std::string str_word_frequency = "WordSortedbyFrequency.txt"; std::ofstream ofs_word_frequency(str_word_frequency.c_str()); // 清除待写入文本内容 if (ofs_word_frequency.good()) { remove(str_word_frequency.c_str()); } word_freq_calculate.write_word_sorted_by_freq(str_word_frequency); // 输出到 WordWithSameSuffix.txt std::string str_word_with_same_suffix = "WordWithSameSuffix.txt"; std::ofstream ofs_word_with_same_suffix(str_word_with_same_suffix.c_str()); // 清除待写入文本内容 if (ofs_word_with_same_suffix.good()) { remove(str_word_with_same_suffix.c_str()); } word_freq_calculate.write_map_file(str_word_with_same_suffix); // 搜索特定单词 word_freq_calculate.search_word("Especially"); word_freq_calculate.search_word("man-at-arms"); word_freq_calculate.search_word("ClionProject"); }
5.8 (待完成)单元测试
5.2.1 测试某单词的存在性
5.2.2 测试准确性检测某单词的频率及具体位置
5.2.3 测试随机选取两个单词对其频率进行比较
5.2.4 测试选择出某前缀或后缀的系列单词
5.2.5 测试特定单词的所有前后缀
5.2.6 测试无意义的虚词代词是否被移除
5.2.7 测试同义词、检测反义词
6 心路历程与收获
首先在程序的实现方面,基本按照需求实现了所有必要的、关机的功能,在程序编写之前花费一定的时间去思考,真的会为之后的程序设计减少不少时间。意识到想要完成一个项目,设计之前的思考是必要的,在编码的过程中,对代码的调试也是十分重要的,掌握一定的调试能力可以在代码运行出错时不至于一个劲的来回看,而是更加有目的性的去查找问题根源,之前对调试程序功能的意识不强,之后会再加强一下调试能力,同时在编码过程中也要注意注释的书写,在完成项目之后,在写报告时,一些变量和具体方法的实现由于没有及时添加相应的注释,都有一些忘了,所以好的编码风格以及合适的注释在编写代码的过程中也是很重要的。
这是我们的第一次结对项目,在磨合方面没有太大的难度,但是面对两个人一起完成一个项目时,还是花了一定的时间去适应,之后就开始了愉快的结对项目啦,李少楠编程方面能力很强,解决了不少难题,在结对项目中付出了很多,两个人一起编程最大的好处是可以从开始设置框架的时候就能够通过讨论得到对于两个人而言的最优方法,以及面对问题时可以一起讨论解决方法,同时两个人一起设计一个项目也可以让代码更加严谨,还有一点是两个人编程似乎能减少一些压力和面对程序设计时出现的问题和Debug时的焦虑感,有一个更加平稳的心态去面对出现的问题,在这样相对个人设计时更加良好的氛围下相信也会有更高效的代码,意识到了结对项目的魅力,以及在结对过程中也学习到了一些对方各自的闪光点,比如在设计或者调试时的一些小技巧或者是面对编程时的认真和不断尝试的态度,总之结对项目体验感很棒,获益匪浅。