`

(转)使用DFA实现文字过滤

 
阅读更多

记得之前面试一个很牛的单位的时候,面试官问了一个问题是关于文字过滤的,当时由于水平有限,能想到的方法就是正则表达式了,最近在学习的过程中发现通过DFA实现文字过滤才是正确的选择,先转载一篇文章,再详细学习。

文字过滤是一般大型网站必不可少的一个功能,而且很多文字类网站更是需要。那么如何设计一个高效的文字过滤系统就是非常重要的了。

文字过滤需求简要描述:判断集合A中哪些子集属于集合B,拿javaeye来说,如果用户发表一篇文章(集合A),我们需要判断这篇文章里是否存在一些关键字是属于集合B,B一般来说就是违禁词列表。

看到这里,没有接触过的同学可能会想到contains,正则之类的方法,但是很遗憾,这些方法都是行不通的。唯一比较好的算法是DFA。

一,DFA简介:
学过编译原理的同学们一定知道,在词法分析阶段将源代码中的文本变成语法的集合就是通过确定有限自动机实现的。但是DFA并不只是词法分析里用到,DFA的用途非常的广泛,并不局限在计算机领域。

DFA的基本功能是可以通过event和当前的state得到下一个state,即event+state=nextstate,
我们来看一张到处都能找到的状态图:


---------------------------------------

 

 
-------------------------------------





大写字母是状态,小写字母是动作:我们可以看到S+a=U,U+a=Q,S+b=V等等。一般情况下我们可以用矩阵来表示整个状态转移过程:
---------------
状态\字符  a       b
S        U       V
U        Q       V
V        U       Q
Q        Q       Q

但是表示状态图可以有很多数据结构,上面的矩阵只是一个便于理解的简单例子。而接下来在本文提到的文字过滤系统中会使用另外的数据结构来实现自动机模型

二,文字过滤
在文字过滤系统中,为了能够应付较高的并发,有一个目标比较重要,就是尽量的减少计算,而在DFA中,基本没有什么计算,有的只是状态的转移。而要把违禁文字列表构造成一个状态机,用矩阵来实现是比较麻烦的,下面介绍一种比较简单的实现方式,就是树结构。

所有的违禁词其本质来说是有ascii码组成的,而待过滤文本其本质也是ascii码的集合,比如说:
输入是A=[101,102,105,97,98,112,110]
违禁词列表:
[102,105]
[98,112]
那么我们的任务就是把上面两个违禁词构造成一个DFA,这样输入的A就可以通过在这个DFA上的转移来实现违禁词查找的功能。

树结构实现这个DFA的基于的基本方法是数组的index和数组value之间的关系(在双数组trie中同样是基于这一基本方法)
那么102其实可以看作一个数组索引,而105是102这个索引指向的下一个数组中的一个索引,105后面没有值了,那就代表这个违禁词结束了。

通过这样一种方式,就可以构造出一颗DFA的树结构表示。

接着遍历输入文本中的每一个byte,然后在DFA中作状态转移就可以判断出一个违禁词是否出现在输入文本中。

下面贴出ahuaxuan基于以上理论用python写的一段文字过滤脚本:

Python代码
  1. #encoding:UTF-8   
  2. import  sys  
  3. from  time  import  time  
  4. '' '''  
  5. @author: ahuaxuan   
  6. @date: 2009-02-20  
  7. '''   
  8.   
  9. wordTree = [None   for  x  in  range( 256 )]  
  10. wordTree.append(0 )  
  11. nodeTree = [wordTree, 0 ]  
  12. def  readInputText():  
  13.     txt = ''   
  14.     for  line  in  open( 'text.txt'  'rb' ):  
  15.         txt = txt + line  
  16.     return  txt  
  17.   
  18. def  createWordTree():  
  19.     awords = []  
  20.     for  b  in  open( 'words.txt'  'rb' ):  
  21.         awords.append(b.strip())  
  22.       
  23.     for  word  in  awords:  
  24.         temp = wordTree  
  25.         for  a  in  range( 0 ,len(word)):  
  26.             index = ord(word[a])  
  27.             if  a < (len(word) -  1 ):  
  28.                 if  temp[index] ==  None :  
  29.                     node = [[None   for  x  in  range( 256 )], 0 ]  
  30.                     temp[index] = node  
  31.                 elif  temp[index] ==  1 :  
  32.                     node = [[None   for  x  in  range( 256 )], 1 ]  
  33.                     temp[index] = node  
  34.                   
  35.                 temp = temp[index][0 ]  
  36.             else :  
  37.                 temp[index] = 1   
  38.       
  39.   
  40. def  searchWord(str):  
  41.     temp = nodeTree  
  42.     words = []  
  43.     word = []  
  44.     a = 0   
  45.     while  a < len(str):  
  46.         index = ord(str[a])  
  47.         temp = temp[0 ][index]  
  48.         if  temp ==  None :  
  49.             temp = nodeTree  
  50.             a = a - len(word)  
  51.             word = []  
  52.         elif  temp ==  1   or  temp[ 1 ] ==  1 :  
  53.             word.append(index)  
  54.             words.append(word)  
  55.             a = a - len(word) + 1    
  56.             word = []  
  57.             temp = nodeTree  
  58.         else :  
  59.             word.append(index)  
  60.         a = a + 1   
  61.       
  62.     return  words  
  63.   
  64. if  __name__ ==  '__main__' :  
  65.     #reload(sys)     
  66.     #sys.setdefaultencoding('GBK')     
  67.     input2 = readInputText()  
  68.     createWordTree();  
  69.     beign=time()  
  70.     list2 = searchWord(input2)  
  71.     print   "cost time : " ,time()-beign  
  72.     strLst = []  
  73.     print   'I have find some words as ' , len(list2)  
  74.     map = {}  
  75.     for  w  in  list2:  
  76.         word = "".join([chr(x) for  x  in  w])  
  77.         if   not  map.__contains__(word):  
  78.             map[word] = 1   
  79.         else :  
  80.             map[word] = map[word] + 1   
  81.       
  82.     for  key, value  in  map.items():  
  83.         print  key, value  



输入文本就是本文(不包含下面的示例结果文本)
运行结果示例:

Java代码
  1. python  5   
  2. 违禁词 12   
  3. DFA 12   
  4. ahuaxuan 3   


        



当然用python实现以上算法只是为了便于理解,事实上python的速度实在是太慢了,同样的违禁词列表,同样的输入文本,python写的比用java写的差了40倍左右。理论上来讲在这个功能上,用python调用c写的功能比较合适。

而这种方式比较大的缺点是内存使用虑较大,因为有很多数组上的元素是None,引用的空间会消耗大量的内存,这个和违禁词的长度和个数成正比。比较好的方式还是用双数组实现DFA,这个方式使用内存空间较小,而基本原理还是一样,通过两个数组的index和value之间的数学关系来实现状态机的转移。

 

分享到:
评论

相关推荐

    使用DFA算法实现的内容安全反垃圾智能鉴黄敏感词过滤

    当用户输入的文字通过这个系统时,DFA会逐字符检查,一旦发现匹配到敏感词的开始,就会立即触发警告或删除该内容。 具体到实现上,这个项目可能包含以下几个关键部分: 1. **敏感词库构建**:首先,需要建立一个...

    thinkphp5敏感词过滤类

    本知识点将聚焦于ThinkPHP5中的一个特定功能——敏感词过滤类,以及如何使用DFA(Deterministic Finite Automaton,确定有限状态自动机)算法来实现这一功能。 首先,我们要理解敏感词过滤的背景。在网站内容管理中...

    基于ik分词和DFA算法的敏感词过滤Java设计源码

    本项目通过实现Java设计源码,结合了当前较为流行的文本处理算法,即ik分词和DFA算法,来达到有效的敏感词过滤效果。 ik分词是基于规则的中文分词技术,它能够对中文文本进行细致的切分,并且支持新词识别,以及...

    胡乱前端高手,一起来学学JS如何实现敏感词过滤

    DFA算法是一种高效的文字匹配方式,尤其适用于敏感词过滤。它通过构建一个有限状态机,将敏感词库转换为状态转移图,从而快速判断字符串中是否存在敏感词。在JavaScript中实现DFA,可以通过创建对象或者数组来模拟...

    Java和Redis实现一个简单的热搜功能

    5. 文字过滤功能: - 在 Java 代码中,定义了一个 `BAD_WORDS` 变量,用于存储需要过滤的关键词列表。如果搜索词匹配到这个列表中的任何关键词,搜索词将被替换为 `FILTERED_WORD`,例如“***”。这里使用了一个...

    Jsp敏感词过滤的示例代码

    大部分论坛、网站等,为了方便管理,都进行了关于敏感词的设定。 在多数网站,敏感词一般是指带有敏感政治倾向(或...在实现文字过滤的算法中,DFA是唯一比较好的实现算法。DFA即Deterministic Finite Automaton,也就

    laravel框架实现敏感词汇过滤功能示例

    本文实例讲述了laravel框架实现敏感词汇过滤功能。分享给大家供大家参考,具体如下: 最近项目有需求,要对用户的签名,回复进行敏感词检测,然后搜到了一个好用的扩展,分享给大家。 ...

    JAVA文本相似度查重代码及示例

    JAVA查重算法,包括HanLP 相似度比较、二叉树、DFA算法实现、敏感词处理工具、IKAnalyzer中文分词工具、分词进行敏感词过滤等查重算法,可以计算海明距离、余弦相似性、莱文斯坦距离、Jaccard 相似度、Sorensen Dice...

    FA:Unicode自动机和正则表达式引擎

    - 通过自定义实现,可以构建基于DFA的正则表达式引擎,实现更快的匹配速度。 6. 自定义Unicode自动机与正则表达式引擎 - 设计和实现非回溯的DFA正则表达式引擎,需要理解正则表达式的状态转换图,并能够处理...

Global site tag (gtag.js) - Google Analytics