為了提高查找效率,這里將敏感詞用樹形結構存儲,每個節點有一個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 }