深夜里终于把代码写完了,由于时间紧促前头没有说自己的进度,这篇文章统一讲讲代码实现过程,下次再讲讲曾经遇到的问题。
最终实现方式:
1、首先确定遍历文件方式,为减少模块间的耦合度,最好先提取出总目录下所有非目录文件的文件名,用string[ ] fileName来存储,方便之后的文件操作。c标准库里提供了一结构体 _finddata_t 来记录文件信息,其中就包含了该文件是否为目录文件,若是,则递归调用遍历函数访问子文件夹;若否,则用 _findnext( ) 函数找下一文件。将访问过的文件的文件名与其目录结合成路径保存在 fileName 中。
2、单词与其出现个数的关系用哈希表来存储,这里采用 C++ STL 函数 unordered_map( ) < string, int > wordValueMap,由于按照要求 file 与 FilE231为同一单词,则再建立一张哈希表 unordered_map( ) < string, string > wordNameMap,其中 wordValueMap 的 string 存储一个单词最简单的名字,即脱去所有后缀数字且字母全部转化为小写,而 wordNameMap 则将单词的最简名字映射到实际出现的且按照要求应该保留的名字。这种方式将查找名字与选取合适的名字分开,可以更方便地进行哈希表查找。
3、对 fileName 中每个文件进行读操作,使用 fgetc( ) 进行逐个字符阅读,并存储在 string word 中,若遇到非字母数字字符,则判断 word 是否以四个字母打头,若是则 word 是一个单词,将其写入 wordValueMap (无则添加,已存在则个数加1),并判断是否要在 wordNameMap 中更改它的实际名字。在此过程中同时记录字符数、单词数、行数(出现“ ”则加1,且每个文件最末尾加1)。为了能够统计词组情况,设置了一全局数组 string allWords[ ],将读文件过程中所有的单词(最简单形式)读入 allWords。
4、输出文件时可直接输出字符数、行数、单词数,遍历 wordValueMap 找到个数最大的10个单词,用数组 string topTenWordName[ ] 和 int topTenWordNum[ ]来保存名字和个数,在遍历过程中通过与 topTenWordNum元素不断比较更新该数组,最终得到10个最频繁单词,其真实名称可以通过查询 wordNameMap 获得。将这10个单词与其个数输出。
5、对于词组,我们用到 allWords数组保存了所有出现过的单词(最简单形式),可遍历该数组并将每相邻的两个单词用“¥”连接组成一新的字符串,即词组,用 unordered_map( ) < string, int > phraseMap 来保存新的字符串的名字与个数。再用与4中相同的方法可以得到最频繁的10个词组。再将这10个词组的名字由“¥”拆开,分别查询 wordNameMap 得到两个单词的真实名称,用 “ ” 连接后重新赋给词组名,将其名称与个数输出到文件。
经过测试集的验证,行数、字符数、单词数与参考答案十分接近,最多存在几百左右的误差。而最频繁的10个单词与词组与参考答案完全一致,出现个数也完全一致。整个程序的运行时间在3分钟以内,比较理想。