上一篇讲了最终的实现方式,但这条路也不是上来就选好的,绕了不少弯路,这次就总结一下曾经遇到的问题。
这次给我最大的经验就是,能不用指针千万别用指针,特别是 char * 这种字符串指针,小型数据集和少量代码的时候还比较好调试,数据集一大简直就是作死。 我一开始选用的哈希表是 hash_map ,这是个非标准化库,不支持 string 类型的默认哈希函数,于是我就用的 hash_map< char* , int >,结果爆出各种错误,每次读入的键值对都会覆盖原来的表中的内容,后来debug才发现,我把单词 char* word 插入hash_map,如果我继续对 word 操作就相当于对表中元素操作。
于是我每次申请一块新的空间 newWord 来保存当前 word 的值再插入表中,这次倒是能正确记录数据,但哈希表的比较函数不能起正确作用(比较函数需人为重载,根据要求判断哪些单词其实是一样的单词),hellow123与Hellow会被认为是不同的单词。并且,一旦跑提供的大测试集 newsample 会出内存错误。这让我懒得再继续调试,果断放弃 hash_map,使用 unordered_map 进行< string , int >映射。同时,在这个过程中我突然想到,与其重载比较函数,不如再建立一张哈希表,用来记录一个单词最简名字(脱去数字且字母全部小写)与真实名字的映射,这样可以将比较函数分离出来维护,同时也为记录词组提供了便利。所以最终我从一开始的 hash_map< char* , int >变为最终的 unordered_map()< string , int > wordValueMap 与 unordered_map< string , string> wordNameMap。
同样的问题在遍历文件夹中也存在,我一开始用一个 char* fileName[ ] 数组来记录每个文件的具体路径,在我自己的测试用例中是没有问题的,然而一旦跑 newsample 这样的大测试集,就会报内存错误。这次作业中我见识了许多种报错,比如停止运行、写入错误、找不到strcat.asm等,这些错误几乎都与内存出错有关,八成是使用了空指针或者数组越界等,这是就要检查数组索引与指针分配的空间。对于char* 这样的指针是很容易出现溢出问题的,所以在遍历大文件夹的时候还是使用 string 比较好,用效率换安全。我在文件夹遍历函数中将 char* fileName[ ]改成了 string fileName[ ], 这样就不用考虑分配内存的问题。需要使用 char* 型参数的函数直接使用 fileName[i].c_str()即可。
我觉得像这次作业这样频繁操作字符串的代码最容易出问题的就是内存管理,如果解决了这些问题具体算法倒不是问题。总结一下,如果追求效率一定要维护好指针的指向,先分配空间再使用,且要清楚地知道它指向的对象,特别要小心别的指针修改了自己指向的空间;如果不想用人脑去管理内存,那就用高度封装的数据类型和STL库吧。