个人项目报告
源码地址: https://github.com/chenzhik/homework1/tree/master/PB16051320
功能要求
1. 实现要求:
对源文件(*.txt,*.cpp,*.h,*.cs,*.html,*.js,*.java,*.py,*.php等,文件夹内的所有文件)统计字符数、单词数、行数、词频,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。
2. 代码要求:
风格规范,使用性能测试工具进行分析,找到性能的瓶颈并改进,对代码进行质量分析,消除所有警告。
3. 过程要求:
按照《构建之法》个人软件开发流程(PSP),记录开发日志
需求分析
1. 统计文件的字符数(只需要统计Ascii码,汉字不用考虑,换行符不用考虑,' '不用考虑)(ascii码大小在[32,126]之间);统计文件的单词总数; 统计文件的总行数(任何字符构成的行,都需要统计)(不要只看换行符的数量,要小心最后一行没有换行符的情形)(空行算一行)。
2. 统计文件中各单词的出现次数,输出频率最高的10个;统计两个单词(词组)在一起的频率,输出频率最高的前10个。
3. 对给定文件夹及其递归子文件夹下的所有文件进行统计。
4. 在Linux系统下,进行代码移植与性能分析 。
设计实现
1. 设计思路
(1) 模块设计(设计文档戳这里)
a. Part 1 — 统计字符、单词、行数可使用全局变量,文件遍历过程中逐个字符读取并判断,再对变量进行自加操作。
b. Part 2 — 单词频数统计,使用HashTable或者map、Hashmap,本鱼比较适应c语言风格所以考虑用哈希表,每个结点内设置计数域和关键字。
c. Part 3 — 词组频数统计与单词同理,哈希表结点内的关键字需要两个,存储相邻两个单词;或者存取单词哈希表两个结点的哈希地址,可能更加高效。
d. Part 4 — 前十频数的单词和词组排序输出,对哈希表进行十次遍历,每次选出频数最大的单词和词组,再对此结点打上已访问标志,总共重复十次。
c. Part 5 — 遍历所有路径文件夹内所有文件,采用_finddata_结构体以及相关函数,先将文件夹内所有文件的路径存入vector<string>内。
(2) 变量类型设计
a. 全局变量累计:使用long int型变量来作为累加器
b. 单词:使用两个string类,分别存储单词缩写和单词尾数,可以使用c++封装好的各种方法,并且节省空间;
2. 代码结构
运行测试
1. 空文件,单个词的文件以及单行文件
2. 典型测试集
包括了1323个文件,*.txt,*.cpp,*.h,*.cs,*.html,*.js,*.java,*.py,*.php等各种类型
3. 性能分析
a. 图一:CPU与GPU利用率
可见GPU使用率有一段大幅下降,追踪到测试集中的长篇小说文件中大量出现三个字母组成的单词,可能由于seek1word读取单词算法中对小于四个字母的“单词”处理不高效导致。
b. 图二:性能分析——CPU采样
可见Seek1Word读取一个单词的函数独占百分比最高,热行集中在这个函数模块
c. 图三:内存占有率
可见当文件遍历至1300至1309号文件时内存消耗急剧上升,原因在于这几个文件是长篇英语小说,词组与单词数非常多。
d. 改进方法:
针对这种情况,我对存储单词的数据结构进行了调整,将原本的200个字节大小的字符数组缩短为150个,因效果不明显于是最后改用string存储单词,大大节省了内存空间;针对Seek1Word函数,精简了一些if-else判断语句,并将temp字符串的初始化放在了此函数外。
核心代码
1. 读取一个单词
1 int Seek1Word(FILE* stream, string &temp) 2 { 3 // variables: 4 // 'ch' recieve the character from the file 5 // 'prelength' return the lengh of the word 6 // 'temp' store the word 7 char ch; 8 int prelength; 9 10 // initialize the variables 11 prelength = 0; 12 ch = '