你的位置:首页 > Java教程

[Java教程]java实现敏感词过滤(DFA算法)


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

在写之前,小Alan给大家推荐一篇来自http://cmsblogs.com/?p=1031的博文,也会参考部分内容来描述博文。

敏感词过滤应该是不用给大家过多的解释吧?讲白了就是你在项目中输入某些字(比如输入xxoo相关的文字时)时要能检测出来,很多项目中都会有一个敏感词管理模块,在敏感词管理模块中你可以加入敏感词,然后根据加入的敏感词去过滤输入内容中的敏感词并进行相应的处理,要么提示,要么高亮显示,要么直接替换成其它的文字或者符号代替。 

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

①查询数据库当中的敏感词,循环每一个敏感词,然后去输入的文本中从头到尾搜索一遍,看是否存在此敏感词,有则做相应的处理,这种方式讲白了就是找到一个处理一个。

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

缺点:这效率让我心中奔过十万匹草泥马,而且匹配的是不是有些蛋疼,如果是英文时你会发现一个很无语的事情,比如英文a是敏感词,那我如果是一篇英文文档,那程序它妹的得处理多少次敏感词?谁能告诉我?

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

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

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

③第三种在这里要特别说明一下,那就是你自己去写一个算法吧,或者在现有的算法的基础上去优化,这也是小Alan追求的至高境界之一,如果哪位淫兄有自己的想法一定别忘了小Alan,可以加小Alan的QQ:810104041教小Alan两招耍耍。

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

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

package com.cfwx.rox.web.sysmgr.util;import java.util.HashMap;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;import com.cfwx.rox.web.common.model.entity.SensitiveWord;/** * 敏感词库初始化 * * @author AlanLee * */public class SensitiveWordInit{  /**   * 敏感词库   */  public HashMap sensitiveWordMap;  /**   * 初始化敏感词   *   * @return   */  public Map initKeyWord(List<SensitiveWord> sensitiveWords)  {    try    {      // 从敏感词集合对象中取出敏感词并封装到Set集合中      Set<String> keyWordSet = new HashSet<String>();      for (SensitiveWord s : sensitiveWords)      {        keyWordSet.add(s.getContent().trim());      }      // 将敏感词库加入到HashMap中      addSensitiveWordToHashMap(keyWordSet);    }    catch (Exception e)    {      e.printStackTrace();    }    return sensitiveWordMap;  }  /**   * 封装敏感词库   *   * @param keyWordSet   */  @SuppressWarnings("rawtypes")  private void addSensitiveWordToHashMap(Set<String> keyWordSet)  {    // 初始化HashMap对象并控制容器的大小    sensitiveWordMap = new HashMap(keyWordSet.size());    // 敏感词    String key = null;    // 用来按照相应的格式保存敏感词库数据    Map nowMap = null;    // 用来辅助构建敏感词库    Map<String, String> newWorMap = null;    // 使用一个迭代器来循环敏感词集合    Iterator<String> iterator = keyWordSet.iterator();    while (iterator.hasNext())    {      key = iterator.next();      // 等于敏感词库,HashMap对象在内存中占用的是同一个地址,所以此nowMap对象的变化,sensitiveWordMap对象也会跟着改变      nowMap = sensitiveWordMap;      for (int i = 0; i < key.length(); i++)      {        // 截取敏感词当中的字,在敏感词库中字为HashMap对象的Key键值        char keyChar = key.charAt(i);        // 判断这个字是否存在于敏感词库中        Object wordMap = nowMap.get(keyChar);        if (wordMap != null)        {          nowMap = (Map) wordMap;        }        else        {          newWorMap = new HashMap<String, String>();          newWorMap.put("isEnd", "0");          nowMap.put(keyChar, newWorMap);          nowMap = newWorMap;        }        // 如果该字是当前敏感词的最后一个字,则标识为结尾字        if (i == key.length() - 1)        {          nowMap.put("isEnd", "1");        }        System.out.println("封装敏感词库过程:"+sensitiveWordMap);      }      System.out.println("查看敏感词库数据:" + sensitiveWordMap);    }  }}

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

package com.cfwx.rox.web.sysmgr.util;import java.util.HashSet;import java.util.Iterator;import java.util.Map;import java.util.Set;/** * 敏感词过滤工具类 * * @author AlanLee * */public class SensitivewordEngine{  /**   * 敏感词库   */  public static Map sensitiveWordMap = null;  /**   * 只过滤最小敏感词   */  public static int minMatchTYpe = 1;  /**   * 过滤所有敏感词   */  public static int maxMatchType = 2;  /**   * 敏感词库敏感词数量   *   * @return   */  public static int getWordSize()  {    if (SensitivewordEngine.sensitiveWordMap == null)    {      return 0;    }    return SensitivewordEngine.sensitiveWordMap.size();  }  /**   * 是否包含敏感词   *   * @param txt   * @param matchType   * @return   */  public static boolean isContaintSensitiveWord(String txt, int matchType)  {    boolean flag = false;    for (int i = 0; i < txt.length(); i++)    {      int matchFlag = checkSensitiveWord(txt, i, matchType);      if (matchFlag > 0)      {        flag = true;      }    }    return flag;  }  /**   * 获取敏感词内容   *   * @param txt   * @param matchType   * @return 敏感词内容   */  public static Set<String> getSensitiveWord(String txt, int matchType)  {    Set<String> sensitiveWordList = new HashSet<String>();    for (int i = 0; i < txt.length(); i++)    {      int length = checkSensitiveWord(txt, i, matchType);      if (length > 0)      {        // 将检测出的敏感词保存到集合中        sensitiveWordList.add(txt.substring(i, i + length));        i = i + length - 1;      }    }    return sensitiveWordList;  }  /**   * 替换敏感词   *   * @param txt   * @param matchType   * @param replaceChar   * @return   */  public static String replaceSensitiveWord(String txt, int matchType, String replaceChar)  {    String resultTxt = txt;    Set<String> set = getSensitiveWord(txt, matchType);    Iterator<String> iterator = set.iterator();    String word = null;    String replaceString = null;    while (iterator.hasNext())    {      word = iterator.next();      replaceString = getReplaceChars(replaceChar, word.length());      resultTxt = resultTxt.replaceAll(word, replaceString);    }    return resultTxt;  }  /**   * 替换敏感词内容   *   * @param replaceChar   * @param length   * @return   */  private static String getReplaceChars(String replaceChar, int length)  {    String resultReplace = replaceChar;    for (int i = 1; i < length; i++)    {      resultReplace += replaceChar;    }    return resultReplace;  }  /**   * 检查敏感词数量   *   * @param txt   * @param beginIndex   * @param matchType   * @return   */  public static int checkSensitiveWord(String txt, int beginIndex, int matchType)  {    boolean flag = false;    // 记录敏感词数量    int matchFlag = 0;    char word = 0;    Map nowMap = SensitivewordEngine.sensitiveWordMap;    for (int i = beginIndex; i < txt.length(); i++)    {      word = txt.charAt(i);      // 判断该字是否存在于敏感词库中      nowMap = (Map) nowMap.get(word);      if (nowMap != null)      {        matchFlag++;        // 判断是否是敏感词的结尾字,如果是结尾字则判断是否继续检测        if ("1".equals(nowMap.get("isEnd")))        {          flag = true;          // 判断过滤类型,如果是小过滤则跳出循环,否则继续循环          if (SensitivewordEngine.minMatchTYpe == matchType)          {            break;          }        }      }      else      {        break;      }    }    if (!flag)    {      matchFlag = 0;    }    return matchFlag;  }}

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

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

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

  /**   * 敏感词过滤   *   * @param text   * @return   */  @RequestMapping(value = "/word/filter")  @ResponseBody  public RespVo sensitiveWordFiltering(String text)  {    RespVo respVo = new RespVo();    try    {      Set<String> set = sensitiveWordService.sensitiveWordFiltering(text);      respVo.setResult(set);    }    catch (Exception e)    {      throw new RoxException("过滤敏感词出错,请联系维护人员");    }    return respVo;  }

  

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