java实现敏感词过滤(DFA算法)

小Alan在最近的开发中遇到了敏感词过滤,便去网上查阅了很多敏感词过滤的资料,在这里也和大家分享一下自己的理解。

 

敏感词过滤应该是不用给大家过多的解释吧?讲白了就是你在项目中输入某些字(比如输入xxoo相关的文字时)时要能检

测出来,很多项目中都会有一个敏感词管理模块,在敏感词管理模块中你可以加入敏感词,然后根据加入的敏感词去过滤输

入内容中的敏感词并进行相应的处理,要么提示,要么高亮显示,要么直接替换成其它的文字或者符号代替。 

 

敏感词过滤的做法有很多,我简单描述我现在理解的几种:

①查询数据库当中的敏感词,循环每一个敏感词,然后去输入的文本中从头到尾搜索一遍,看是否存在此敏感词,有则做相

应的处理,这种方式讲白了就是找到一个处理一个。

优点:so easy。用java代码实现基本没什么难度。

缺点:这效率让我心中奔过十万匹草泥马,而且匹配的是不是有些蛋疼,如果是英文时你会发现一个很无语的事情,比如英文

a是敏感词,那我如果是一篇英文文档,那程序它妹的得处理多少次敏感词?谁能告诉我?

②传说中的DFA算法(有穷自动机),也正是我要给大家分享的,毕竟感觉比较通用,算法的原理希望大家能够自己去网上查查

资料,这里就不详细说明了。

优点:至少比上面那sb效率高点。

缺点:对于学过算法的应该不难,对于没学过算法的用起来也不难,就是理解起来有点gg疼,匹配效率也不高,比较耗费内存,

敏感词越多,内存占用的就越大。

③第三种在这里要特别说明一下,那就是你自己去写一个算法吧,或者在现有的算法的基础上去优化,这也是小Alan追求的至

高境界之一,如果哪位淫兄有自己的想法一定别忘了小Alan,可以加小Alan的QQ:810104041教小Alan两招耍耍。

 

那么,传说中的DFA算法是怎么实现的呢?

第一步:敏感词库初始化(将敏感词用DFA算法的原理封装到敏感词库中,敏感词库采用HashMap保存),代码如下:

  1 package com.cfwx.rox.web.sysmgr.util;
  2 
  3 import java.util.HashMap;
  4 import java.util.HashSet;
  5 import java.util.Iterator;
  6 import java.util.List;
  7 import java.util.Map;
  8 import java.util.Set;
  9 
 10 import com.cfwx.rox.web.common.model.entity.SensitiveWord;
 11 
 12 /**
 13  * 敏感词库初始化
 14  * 
 15  * @author AlanLee
 16  *
 17  */
 18 public class SensitiveWordInit
 19 {
 20     /**
 21      * 敏感词库
 22      */
 23     public HashMap sensitiveWordMap;
 24 
 25     /**
 26      * 初始化敏感词
 27      * 
 28      * @return
 29      */
 30     public Map initKeyWord(List<SensitiveWord> sensitiveWords)
 31     {
 32         try
 33         {
 34             // 从敏感词集合对象中取出敏感词并封装到Set集合中
 35             Set<String> keyWordSet = new HashSet<String>();
 36             for (SensitiveWord s : sensitiveWords)
 37             {
 38                 keyWordSet.add(s.getContent().trim());
 39             }
 40             // 将敏感词库加入到HashMap中
 41             addSensitiveWordToHashMap(keyWordSet);
 42         }
 43         catch (Exception e)
 44         {
 45             e.printStackTrace();
 46         }
 47         return sensitiveWordMap;
 48     }
 49 
 50     /**
 51      * 封装敏感词库
 52      * 
 53      * @param keyWordSet
 54      */
 55     @SuppressWarnings("rawtypes")
 56     private void addSensitiveWordToHashMap(Set<String> keyWordSet)
 57     {
 58         // 初始化HashMap对象并控制容器的大小
 59         sensitiveWordMap = new HashMap(keyWordSet.size());
 60         // 敏感词
 61         String key = null;
 62         // 用来按照相应的格式保存敏感词库数据
 63         Map nowMap = null;
 64         // 用来辅助构建敏感词库
 65         Map<String, String> newWorMap = null;
 66         // 使用一个迭代器来循环敏感词集合
 67         Iterator<String> iterator = keyWordSet.iterator();
 68         while (iterator.hasNext())
 69         {
 70             key = iterator.next();
 71             // 等于敏感词库,HashMap对象在内存中占用的是同一个地址,所以此nowMap对象的变化,sensitiveWordMap对象也会跟着改变
 72             nowMap = sensitiveWordMap;
 73             for (int i = 0; i < key.length(); i++)
 74             {
 75                 // 截取敏感词当中的字,在敏感词库中字为HashMap对象的Key键值
 76                 char keyChar = key.charAt(i);
 77 
 78                 // 判断这个字是否存在于敏感词库中
 79                 Object wordMap = nowMap.get(keyChar);
 80                 if (wordMap != null)
 81                 {
 82                     nowMap = (Map) wordMap;
 83                 }
 84                 else
 85                 {
 86                     newWorMap = new HashMap<String, String>();
 87                     newWorMap.put("isEnd", "0");
 88                     nowMap.put(keyChar, newWorMap);
 89                     nowMap = newWorMap;
 90                 }
 91 
 92                 // 如果该字是当前敏感词的最后一个字,则标识为结尾字
 93                 if (i == key.length() - 1)
 94                 {
 95                     nowMap.put("isEnd", "1");
 96                 }
 97                 System.out.println("封装敏感词库过程:"+sensitiveWordMap);
 98             }
 99             System.out.println("查看敏感词库数据:" + sensitiveWordMap);
100         }
101     }
102 }

 

第二步:写一个敏感词过滤工具类,里面可以写上自己需要的方法,代码如下:

  1 package com.cfwx.rox.web.sysmgr.util;
  2 
  3 import java.util.HashSet;
  4 import java.util.Iterator;
  5 import java.util.Map;
  6 import java.util.Set;
  7 
  8 /**
  9  * 敏感词过滤工具类
 10  * 
 11  * @author AlanLee
 12  *
 13  */
 14 public class SensitivewordEngine
 15 {
 16     /**
 17      * 敏感词库
 18      */
 19     public static Map sensitiveWordMap = null;
 20 
 21     /**
 22      * 只过滤最小敏感词
 23      */
 24     public static int minMatchTYpe = 1;
 25 
 26     /**
 27      * 过滤所有敏感词
 28      */
 29     public static int maxMatchType = 2;
 30 
 31     /**
 32      * 敏感词库敏感词数量
 33      * 
 34      * @return
 35      */
 36     public static int getWordSize()
 37     {
 38         if (SensitivewordEngine.sensitiveWordMap == null)
 39         {
 40             return 0;
 41         }
 42         return SensitivewordEngine.sensitiveWordMap.size();
 43     }
 44 
 45     /**
 46      * 是否包含敏感词
 47      * 
 48      * @param txt
 49      * @param matchType
 50      * @return
 51      */
 52     public static boolean isContaintSensitiveWord(String txt, int matchType)
 53     {
 54         boolean flag = false;
 55         for (int i = 0; i < txt.length(); i++)
 56         {
 57             int matchFlag = checkSensitiveWord(txt, i, matchType);
 58             if (matchFlag > 0)
 59             {
 60                 flag = true;
 61             }
 62         }
 63         return flag;
 64     }
 65 
 66     /**
 67      * 获取敏感词内容
 68      * 
 69      * @param txt
 70      * @param matchType
 71      * @return 敏感词内容
 72      */
 73     public static Set<String> getSensitiveWord(String txt, int matchType)
 74     {
 75         Set<String> sensitiveWordList = new HashSet<String>();
 76 
 77         for (int i = 0; i < txt.length(); i++)
 78         {
 79             int length = checkSensitiveWord(txt, i, matchType);
 80             if (length > 0)
 81             {
 82                 // 将检测出的敏感词保存到集合中
 83                 sensitiveWordList.add(txt.substring(i, i + length));
 84                 i = i + length - 1;
 85             }
 86         }
 87 
 88         return sensitiveWordList;
 89     }
 90 
 91     /**
 92      * 替换敏感词
 93      * 
 94      * @param txt
 95      * @param matchType
 96      * @param replaceChar
 97      * @return
 98      */
 99     public static String replaceSensitiveWord(String txt, int matchType, String replaceChar)
100     {
101         String resultTxt = txt;
102         Set<String> set = getSensitiveWord(txt, matchType);
103         Iterator<String> iterator = set.iterator();
104         String word = null;
105         String replaceString = null;
106         while (iterator.hasNext())
107         {
108             word = iterator.next();
109             replaceString = getReplaceChars(replaceChar, word.length());
110             resultTxt = resultTxt.replaceAll(word, replaceString);
111         }
112 
113         return resultTxt;
114     }
115 
116     /**
117      * 替换敏感词内容
118      * 
119      * @param replaceChar
120      * @param length
121      * @return
122      */
123     private static String getReplaceChars(String replaceChar, int length)
124     {
125         String resultReplace = replaceChar;
126         for (int i = 1; i < length; i++)
127         {
128             resultReplace += replaceChar;
129         }
130 
131         return resultReplace;
132     }
133 
134     /**
135      * 检查敏感词数量
136      * 
137      * @param txt
138      * @param beginIndex
139      * @param matchType
140      * @return
141      */
142     public static int checkSensitiveWord(String txt, int beginIndex, int matchType)
143     {
144         boolean flag = false;
145         // 记录敏感词数量
146         int matchFlag = 0;
147         char word = 0;
148         Map nowMap = SensitivewordEngine.sensitiveWordMap;
149         for (int i = beginIndex; i < txt.length(); i++)
150         {
151             word = txt.charAt(i);
152             // 判断该字是否存在于敏感词库中
153             nowMap = (Map) nowMap.get(word);
154             if (nowMap != null)
155             {
156                 matchFlag++;
157                 // 判断是否是敏感词的结尾字,如果是结尾字则判断是否继续检测
158                 if ("1".equals(nowMap.get("isEnd")))
159                 {
160                     flag = true;
161                     // 判断过滤类型,如果是小过滤则跳出循环,否则继续循环
162                     if (SensitivewordEngine.minMatchTYpe == matchType)
163                     {
164                         break;
165                     }
166                 }
167             }
168             else
169             {
170                 break;
171             }
172         }
173         if (!flag)
174         {
175             matchFlag = 0;
176         }
177         return matchFlag;
178     }
179 
180 }

 

第三步:一切都准备就绪,当然是查询好数据库当中的敏感词,并且开始过滤咯,代码如下:

 1  @SuppressWarnings("rawtypes")
 2     @Override
 3     public Set<String> sensitiveWordFiltering(String text)
 4     {
 5         // 初始化敏感词库对象
 6         SensitiveWordInit sensitiveWordInit = new SensitiveWordInit();
 7         // 从数据库中获取敏感词对象集合(调用的方法来自Dao层,此方法是service层的实现类)
 8         List<SensitiveWord> sensitiveWords = sensitiveWordDao.getSensitiveWordListAll();
 9         // 构建敏感词库
10         Map sensitiveWordMap = sensitiveWordInit.initKeyWord(sensitiveWords);
11         // 传入SensitivewordEngine类中的敏感词库
12         SensitivewordEngine.sensitiveWordMap = sensitiveWordMap;
13         // 得到敏感词有哪些,传入2表示获取所有敏感词
14         Set<String> set = SensitivewordEngine.getSensitiveWord(text, 2);
15         return set;
16     }

 

最后一步:在Controller层写一个方法给前端请求,前端获取到需要的数据并进行相应的处理,代码如下:

 1  /**
 2      * 敏感词过滤
 3      * 
 4      * @param text
 5      * @return
 6      */
 7     @RequestMapping(value = "/word/filter")
 8     @ResponseBody
 9     public RespVo sensitiveWordFiltering(String text)
10     {
11         RespVo respVo = new RespVo();
12         try
13         {
14             Set<String> set = sensitiveWordService.sensitiveWordFiltering(text);
15             respVo.setResult(set);
16         }
17         catch (Exception e)
18         {
19             throw new RoxException("过滤敏感词出错,请联系维护人员");
20         }
21 
22         return respVo;
23     }

 

小Alan在代码中写了不少的注释,希望大家能够动动自己的脑筋好好的理解一下。

 

可爱博主:AlanLee

博客地址:http://www.cnblogs.com/AlanLee

本文出自博客园,欢迎大家加入博客园。

posted @ 2016-03-28 16:35  AlanLee-Java  阅读(27037)  评论(4编辑  收藏