淺析敏感詞過濾算法(C++)


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

接下來實現這個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 };
Tree.h
 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.cpp

最后就是利用上述的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 };
Filter.h
 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.cpp

最后就是調用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 }
Process.cpp

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM