个人作业:词频统计

Posted USTC火苗

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了个人作业:词频统计相关的知识,希望对你有一定的参考价值。

 

                 个人作业:词频统计

Github项目地址:https://github.com/USTChuomiao/homework1/blob/master/PB15061351/hw1.cpp

 

PSP

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning 计划 5 3
· Estimate · 估计这个任务需要多少时间 5 3
Development 开发 1125 1235
· Analysis · 需求分析 (包括学习新技术) 3 * 60 4* 60
· Design Spec · 生成设计文档 30 40
· Design Review · 设计复审 (和同事审核设计文档) 5 5
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
· Design · 具体设计 30 30
· Coding · 具体编码 8 * 60 9 * 60
· Code Review · 代码复审 30 40
· Test · 测试(自我测试,修改代码,提交修改) 6* 60 5.5 * 60
Reporting 报告 60 60
· Test Report · 测试报告 20 30
· Size Measurement · 计算工作量 10 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 20
合计   1190 1298

 

项目要求:

1、目标:对源文件(*.txt,*.cpp,*.h,*.cs,*.html,*.js,*.java,*.py,*.php 等,文件夹内的 所有文件)统计字符数、单词数、行数、词频,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。

2、要求:设计 10 个测试样例用于测试,确保程序正常运行(例如:空文件,只包含一 个词的文件,只有一行的文件,典型文件等等)。

 

解决思路:

1、首先做了一个初步的规划: (1)文件夹的遍历;

              (2)统计单个文件中的字符数、行数、单词数;

              (3)对每个单词以及出现次数进行存储;

              (4)对每个词组以及出现次数进行存储;

              (5)找出词频前十的单词和词组并输出。

2、完成对一个路径内所有文件的遍历。起初在windows环境开发,学习了网上的一些操作之后,结合自己的想法,决定用栈来辅助所有文件的遍历。先打开一个文件夹,如果是文件夹将其路径存入栈中,如果是文件继续遍历,完成当前文件夹遍历之后,从栈中弹出一个路径继续重复上述操作,直到栈空即所有文件都被遍历了。后期将代码移植到 linux 环境下,采用递归的方法遍历所有文件。

3、统计一个文件中字符数、行数、单词数,字符数和行数没什么好说的,主要是单词的提取。根据给出的单词的定义,设计了一个提取单词的算法:把当前字符用ch来存储,它的前一个字符用chr来存储,首先若满足word[0]为空、chr为非数字和字母的分隔符、ch为字母这三个条件,则将ch存入word[0];继续读取字符,若word不为空且ch为字母,则存入word,若word长度大于4,ch为数字亦存入word;当ch读到一个分隔符时,即不满足上述条件时,检测当前word长度是否大于4,若大于则成功提取一个单词,反之清空word;重复上述操作即可将文件中单词提取出来。

4、存储并统计单词出现次数,起初考虑了用结构体数组、文件、树来存储,但是想了想感觉存储空间或者运行速度上会受到限制,最后采用Hash Table通过动态链表的形式存储单词及其出现次数。这个Hash Table最初想到以26个字母对应26个头指针的形式,后来升级到26*26*26*26个头指针,即依据单词的前四个字母进行检索。从刚刚成功提取单词那里调用一个对单词进行处理的函数,这个函数大概算法是这样的:首先找到这个单词对应的头指针,然后顺着头指针的链表依次对比两个单词的大小(通过ASCII码),直到找到一个相同单词的位置,就把他的词频数+1,或者找到介于两单词之间,就创建节点插入链表(这个过程中相同有是否替换单词形式的判断过程)。直到把所有单词都存入Hash Table即完成任务。(这样存储进去的单词若从Hash Table头开始遍历即是词典顺序)

5、词组的统计和单词类似,只需对提取单词的操作稍加改动,即可完成词组的提取。同样词组存入词组的Hash Table的操作也同单词类似。

6、输出词频前十的单词和词组,首先提取所有词频存入数组(单词和词组分开操作,并过滤掉相同词频数),找出词频数最大的十个,然后由刚找到的前十词频去遍历Hash Table输出十个单词或词组就停止。(注意:十个词频数不一定全用到,可能有两个或多个单词或词组词频数相同)

 

Linux下对代码质量进行分析:

gprof是gcc/g++编译器支持的一种性能诊断工具。只要在编译时加上-pg选项,编译器就会在编译程序时在每个函数的开头加一个mcount函数调用,在每一个函数调用之前都会先调用这个mcount函数,在mcount中会保存函数的调用关系图和函数的调用时间和被调次数等信息。最终在程序退出时保存在gmon.out文件中。

这次代码做的主要优化是Hash Table的长度改变,从 26 变成 26*26 到最终的 26*26*26*26 可以明显的感觉到速度的提升。

还有一些小的优化就是词组提取过程中要访问单词的哈希表,起初采用了整个哈希表遍历的方式,后来发现只需要遍历对应行的单链表即可,也节省了不少时间。

下边这些数据是优化后的代码数据。

1、> g++ -pg -g -o h hw1.cpp

   > ./h code/test6

运行完之后生成了 gmon.out 文件。

2、> gprof h gmon.out -p

 

得到了每个函数的性能参数,时间占比什么的。。。

3、> gprof h gmon.out -q

得到了函数之间的调用关系。。。

4、> gprof h gmon.out -A

 

 测试用例及分析过程:

 根据作业的要求,设计了十个测试集,完成并验证了符合作业要求。(这里只列出六个做以说明。)

test1:多层多个不含任何文件的空文件夹集。

测试结果:所有结果都是0,测试正常,结果正确。

test2:文件夹中仅包含三个空文件。

测试结果:字符数、行数、单词数都是0,文件数:3,测试正常,结果正确。

test3:test3.txt文件内容:file123  123file File///FILE;    (同时也算测试了路径为单个文件程序也能照常运行)

测试结果:123file没有被算作单词,file123、FILE、File是同一个单词FILE,测试正常,结果正确。

test4:包含windows95、windows7、windows、windows32a、iPhone4、IPhone5的测试集。

测试结果:windows95、windows7都看作是和windows是同一单词,但和windoes32a不是同一单词;

iPhone4和IPhone5被认为是IPone5,测试正常,结果正确。

 

test5:文件夹中仅包含4个.txt大文件。

测试结果:和某同学对相同文件夹测试得到结果基本相同。测试正常,结果正确。

test6:文件夹中包含各种类型的文件。

测试结果:和某同学对相同文件夹测试得到结果基本相同。测试正常,结果正确。

 

  此次项目获得的经验:

在此次项目的开发测试过程中,首先对问题的分析和解决方案的选取很重要,要把解决策略步骤化,代码模块化,并作模块化调试之后再联合调试。其次在写程序的时候要不断学习吸收新的知识,另外在debug的时候最好有个伙伴在旁边,这样能帮助你很快的解决一些小问题,节省很多时间。最后在完成初步调试通过后,要进行多方面多层次的大量测试,从中发现问题并改进,以确保代码的稳定性和正确性。

以上是关于个人作业:词频统计的主要内容,如果未能解决你的问题,请参考以下文章

软件工程个人作业 词频统计

个人作业——词频统计

个人作业——词频统计

第一次个人作业之词频统计

个人第二次作业

作业4:结对项目—— 词频统计