——论一条咸鱼的自我修养

1· 项目要求

1.1 基本功能

  1. 对源文件(*.txt,*.cpp,*.h,*.cs,*.html,*.js,*.java,*.py,*.php 等,文件夹内的所有文件)统计字符数、单词数、行数、词频,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。
  2. 使用性能测试工具进行分析,找到性能的瓶颈并改进。
  3. 对代码进行质量分析,消除所有警告。
  4. 设计 10 个测试样例用于测试,确保程序正常运行(如:空文件,只含一个词的文件,只有一行的文件,典型文件等)。
  5. 使用 Github 进行代码管理。
  6. Linux 下性能分析(附加任务)。
  7. 撰写博客。

1.2 注意事项 

a) 需要统计的字符等价于 ASCII 码值属于 [32,126] 区间。

b) 单词的定义:至少以 4 个英文字母开头,跟上字母数字符号;单词以分隔符(非字母数字符号)分割;不分大小写;如果两个单词只有最后的数字结尾不同,则认为等价;等价单词输出按字典顺序;单词长度只需要考虑 [4, 1024],超出此范围的不用统计。

c) 词组的定义:同一文件内,相邻两个合法的单词组成词组;若两单词分别均等价,则为同一词组;按字典顺序输出。

d) 输入文件名以命令行参数传入。需要遍历整个文件夹时,则要输入文件夹的路径。

e) 根据命令行参数判断是否为目录。

f) 将所有文件中的词汇,进行统计,最终只输出一个整体的词频统计结果输出文件 result.txt。

 

2· 思路历程

2.1 遍历文件夹

起初,并不知道 C/C++ 程序下如何打开指定路径的目录,上网查询后得知在 <io.h> 下有可以使用的库函数。其中,结构体_finddata_t 用于存储文件信息,还需要用到 _findfirst、_findnext、_findclose 这三个函数。仔细学习了结构体各分量意义、函数参数传递、返回值,然后掌握了打开文件的方法,再结合递归调用即可,详见后面。

2.2 读取单词

由于还需要统计所有合法字符数,行数,所以一开始思路是用 fgetc(),在打开文件之后逐字符读取,字符数通过 ASCII 码值统计,而行数可以构造映射除第一行外每一个换行符开启新的一行,所以每个文件行数即为换行符数加一。而对于单词数,可以边读字符数的时候,便进行分析判断合法的单词并统计。

接下来的问题就是如何确定存储数据结构。一开始,我查询过字典树的相关知识,其优点是便于排序和查找,但是修改起来较为繁琐况且我们并不需要排序,故弃之。接着,考虑过哈希表,哈希对于解决我们的需求有很大优势,但是又觉得写起来麻烦,需要构造哈希函数和冲突解决方式,又弃之。然后受到单词前四个字符必须是字母的启发,想到了将前面四个字母与四位 26 进制数进行一一映射,构造结构体储存单词信息,开辟长度为 26*26*26*26 的结构体指针数组,对于每个单词通过首四字母计算唯一的地址,对于首四字母相同的单词在该地址下进行链表字典有序查找、插入和修改。详情参见后。

2.3 读取词组

对于读取词组这种要求,一开始我的内心其实是拒绝的,所以先想着先把单词解决。殊不知,解决完单词之后,尴尬的境地才出现了。其中我历经了两个彻底不同的处理词组代码的实现。

原始版本下,由于单词结构体已经开辟,不想再为词组申请大量存储空间,我在单词结构体之下增加一个分量,每次读取一个单词,顺便拉一条链指向该单词上一位出现过的所有单词,由此存储词组信息,然后处境异常尴尬,出现了情理之中的恰如其分的繁琐,更尴尬的就是我静下心来也完成了统计,词组 top10 结果均正确,运行也是情理之中的恰如其分的慢。代码整合后已经到了星期四中午,但我还是决定重新处理这部分。

优化版本下,我还是改用了哈希表的方式处理词组,因为我查到了 C++ 的 STL 库中有 unordered_map 的库可以用,之前并没有接触过,甚至 C++ 都不熟,还是学习了一下这其中的类和对象,然后重载方法,定义词组结构体,里面有指向两单词的指针和该词组数,将该结构体自定义为 hash key,然后又自定义 hash 函数和 hash 比较函数,使用 unordered_map 处理词组。

2.4 频率统计

由于一开始对革命路线错误的估计和判断,很单纯地觉得庞大的数量用遍历肯定不好,所以在一边读取的时候一边更新 top10,后来才意识到,实时更新复杂度更高,因为读完后很多单词都重复出现,而在实时读取更新的时候则浪费了比较时间。

 

3· 具体实现

3.1 数据结构

3.1.1 单词  (原始版) 

1 struct wordsdata          //存放单词信息
2 {
3     char words[1024];           //单词字符串 
4     int number;                 //单词数量
5     wordsdata *next;
6     phrase *prehead;            //指向词组
7 };
struct wordsdata

相关文章:

  • 2021-10-18
  • 2022-01-22
  • 2022-02-24
  • 2021-11-17
  • 2021-10-14
  • 2022-03-01
  • 2021-08-16
猜你喜欢
  • 2021-09-16
  • 2022-12-23
  • 2021-10-09
  • 2021-07-22
  • 2021-12-17
  • 2021-06-13
  • 2021-07-10
相关资源
相似解决方案