为了提高查找效率,这里将敏感词用树形结构存储,每个节点有一个map成员,其映射关系为一个string对应一个TreeNode。
STL::map是按照operator<比较判断元素是否相同,以及比较元素的大小,然后选择合适的位置插入到树中。为了提高map的插入及查询效率,可以选用hash_map或unordered_map。关于他们的效率,可以参考http://blog.csdn.net/whizchen/article/details/9286557。
下面主要实现了TreeNode类,进行节点的插入以及查询。(这里命名用Trie比较专业)
1 #include<map> 2 #include<string> 3 //#include<unordered_map> 4 using namespace std; 5 6 7 class Tree; 8 class TreeNode 9 { 10 friend class Tree; 11 typedef map<string,TreeNode> _TreeMap; 12 typedef map<string,TreeNode>::iterator _TreeMapIterator; 13 // typedef unordered_map<string,TreeNode> _TreeMap; 14 // typedef unordered_map<string,TreeNode>::iterator _TreeMapIterator; 15 private: 16 string m_character; 17 _TreeMap m_map; 18 TreeNode* m_parent; 19 public: 20 TreeNode(string character); 21 TreeNode(){ 22 m_character=""; 23 }; 24 string getCharacter() const; 25 TreeNode* findChild(string& nextCharacter); 26 TreeNode* insertChild(string& nextCharacter); 27 28 };
#include <iostream> #include "TreeNode.h" using namespace std; string TreeNode::getCharacter() const { return m_character; } TreeNode::TreeNode(string character) { if (character.size() == 2) m_character.assign(character); else cout<<"error"; } TreeNode* TreeNode::findChild(string& nextCharacter) { _TreeMapIterator TreeMapIt = m_map.find(nextCharacter); //利用map/unordered_map进行查找 return (TreeMapIt == m_map.end()) ? NULL :&TreeMapIt->second; return NULL; } TreeNode* TreeNode::insertChild(string& nextCharacter) { if(!findChild(nextCharacter)) //添加节点,并返回节点位置 { m_map.insert(pair<string, TreeNode>(nextCharacter, TreeNode(nextCharacter))); return &(m_map.find(nextCharacter)->second); } return NULL; }
接下来实现这个tree,在建立TreeNode树时,以parent为根节点建立,一开始parent为m_emptyRoot,然后把keyword按照规则添加到树中,假设一开始m_emptyRoot为空,keyword为"敏感词",则会以"敏感词"为一条分支建立成为一颗树枝'敏'->'感'->'词',此后,若想再添加"敏感度",由于"敏感词"与"敏感度"的前两个字相同,则会在'敏'->'感'->'词'的基础上,从字'感'开始新生长出一颗分支,即'敏'->'感'->'度',这两颗分支共用'敏'->'感'。
程序中暂时考虑中文的情况,如果需要考虑英文或中英文结合的情况,将PACE改为1,另外程序做出部分修改即可。
下面代码实现了Tree类,进行树的构成及查询。
1 #include "TreeNode.h" 2 using namespace std; 3 4 class Tree 5 { 6 public: 7 int count; //当前查找的一个敏感词的字数 8 TreeNode* insert(string& keyword); 9 TreeNode* insert(const char* keyword); 10 TreeNode* find(string& keyword); 11 Tree(){ 12 count = 0; 13 }; 14 private: 15 TreeNode m_emptyRoot; 16 int m_pace; 17 TreeNode* insert(TreeNode* parent, string& keyword); 18 TreeNode* insertBranch(TreeNode* parent, string& keyword); 19 TreeNode* find(TreeNode* parent,string& keyword); 20 21 };
1 #include "Tree.h" 2 #include<iostream> 3 4 #define PACE 2 //如果需要考虑英文或中英文结合的情况,将PACE改为1,另外程序还需要做部分修改 5 6 TreeNode* Tree::insert(string& keyword) 7 { 8 return insert(&m_emptyRoot, keyword); 9 } 10 11 TreeNode* Tree::insert(const char* keyword) 12 { 13 string wordstr(keyword); 14 return insert(wordstr); 15 } 16 17 18 TreeNode* Tree::insert(TreeNode* parent, string& keyword) 19 { 20 if(keyword.size()==0) 21 return NULL; 22 string firstChar=keyword.substr(0,PACE); 23 TreeNode* firstNode = parent->findChild(firstChar); 24 if(firstNode==NULL) 25 return insertBranch(parent,keyword); 26 string restChar=keyword.substr(PACE,keyword.size()); 27 return insert(firstNode,restChar); 28 } 29 30 TreeNode* Tree::insertBranch(TreeNode* parent,string& keyword) 31 { 32 string firstChar=keyword.substr(0,PACE); 33 TreeNode* firstNode = parent->insertChild(firstChar); 34 if(firstNode!=NULL) 35 { 36 string restChar=keyword.substr(PACE,keyword.size()); 37 if(!restChar.empty()) 38 return insertBranch(firstNode,restChar); 39 } 40 return NULL; 41 } 42 43 TreeNode* Tree::find(string& keyword) 44 { 45 return find(&m_emptyRoot,keyword); 46 } 47 48 49 TreeNode* Tree::find(TreeNode* parent,string& keyword) 50 { 51 string firstChar=keyword.substr(0,PACE); 52 TreeNode* firstNode = parent->findChild(firstChar); 53 if(firstNode==NULL) //未找到 54 { 55 count=0; 56 return NULL; 57 } 58 string restChar=keyword.substr(PACE,keyword.size()); 59 if(firstNode->m_map.empty()) //对应词组结束,则判断该词为敏感词 60 { 61 //std::cout<<count+1<<endl; 62 return firstNode; 63 } 64 if(keyword.size()==PACE) //最后一个字 65 return NULL; 66 count++; 67 return find(firstNode,restChar); 68 }
最后就是利用上述的Tree来实现敏感词过滤,Filter::censor(string& source)函数用来进行敏感词过滤,source即输入的字符串,如果source包含敏感词,则用"**"代替掉。
Filter::load(const char* filePath)函数通过文件载入敏感词,并构建Tree。
为使实现简单,代码中过滤了英文数字及一些符号,让敏感词库的词能全部被识别。这里有2个问题遗留下来:
1.需要考虑英文,已经中英文结合的敏感词。程序还需要作出一定修改;
2.载入文件后,可对敏感词做出一定优化。
下面代码实现了Filter类,调用函数实现敏感词过滤。
1 #include <string> 2 #include "Tree.h" 3 4 class Filter 5 { 6 private: 7 Tree m_tree; 8 9 public: 10 void load(const char* fileName); 11 bool m_initialized; 12 void censor(string& source); 13 };
1 #include <iostream> 2 #include <fstream> 3 #include "Filter.h" 4 5 void Filter::load(const char* filePath) 6 { 7 ifstream keywordsFile(filePath, ios::in); 8 if (keywordsFile.is_open()) 9 { 10 char buffer[256]; 11 int count = 0; 12 int offset = 0; 13 while((buffer[offset]=keywordsFile.get())!=EOF) 14 { 15 if((buffer[offset]>='a'&&buffer[offset]<='z')|| 16 (buffer[offset]>='A'&&buffer[offset]<='Z')|| 17 (buffer[offset]>='0'&&buffer[offset]<='9')|| 18 buffer[offset]=='\'') 19 continue; 20 string word1; 21 word1.assign(buffer,offset); 22 if(buffer[offset]==','&&(offset%2)==0) 23 { 24 string word; 25 if(offset) 26 { 27 word.assign(buffer,offset); 28 m_tree.insert(word); 29 } 30 offset = 0; 31 } 32 else 33 offset++; 34 } 35 } 36 keywordsFile.close(); 37 m_initialized = true; 38 } 39 40 41 void Filter::censor(string& source) 42 { 43 if (!m_initialized) 44 { 45 cout<<"没有载入关键词"; 46 return; 47 } 48 else 49 { 50 int length = source.size(); 51 for (int i = 0; i < length; i += 2) 52 { 53 string substring = source.substr(i, length - i); 54 if (m_tree.find(substring) != NULL) //发现敏感词 55 { 56 cout<<substring.substr(0,(m_tree.count+1)*2)<<endl; 57 source.replace(i,(m_tree.count+1)*2,"**"); 58 length = source.size(); 59 } 60 } 61 } 62 }
最后就是调用Filter类,通过文件输入,并将过滤的结果输出到文件,并输出用时。
1 #include<iostream> 2 #include<string> 3 #include<list> 4 #include <fstream> 5 #include "Filter.h" 6 #include <sys/timeb.h> 7 using namespace std; 8 9 void main() 10 { 11 Filter filter; 12 string str; 13 filter.load("keywords.txt"); 14 ifstream inputFile("input.txt",ios::in); 15 inputFile>>str; 16 inputFile.close(); 17 ofstream outputFile("output.txt",ios::out); 18 struct timeb startTime,endTime; 19 ftime(&startTime); 20 for(int i=0;i<1000;i++) 21 { 22 filter.censor(str); 23 } 24 ftime(&endTime); 25 cout<<str<<endl; 26 cout<<"查询用时:"<<(endTime.time-startTime.time)*1000 + 27 (endTime.millitm - startTime.millitm)<<"ms"<<endl; 28 outputFile<<str<<endl; 29 outputFile<<"查询用时:"<<(endTime.time-startTime.time)*1000 + 30 (endTime.millitm - startTime.millitm)<<"ms"; 31 outputFile.close(); 32 }