java利用DFA算法实现敏感词过滤功能

 更新时间:2017年06月21日 10:06:31   作者:AlanLee  
在最近的开发中遇到了敏感词过滤,便去网上查阅了很多敏感词过滤的资料,在这里也和大家分享一下自己的理解。下面这篇文章主要给大家介绍了关于java利用DFA算法实现敏感词过滤功能的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。

前言

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

那么,传说中的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;
  }

总结

以上就是这篇文章的全部内容了,代码中写了不少的注释,大家可以动动自己的脑筋好好的理解一下。希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

相关文章

  • Java线程池中多余的线程是如何回收的

    Java线程池中多余的线程是如何回收的

    对于经常使用第三方框架进行web开发的程序员来说,Java线程池理所应当是非常智能的,那么Java线程池中多余的线程是如何回收的?本文就来介绍一下
    2021-05-05
  • SpringBoot整合MongoDB完整实例代码

    SpringBoot整合MongoDB完整实例代码

    本文主要介绍了SpringBoot整合MongoDB完整实例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • Java中MyBatis传入参数parameterType问题

    Java中MyBatis传入参数parameterType问题

    这篇文章主要介绍了Java中MyBatis传入参数parameterType问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • mybatisplus中返回Vo的案例讲解

    mybatisplus中返回Vo的案例讲解

    这篇文章主要介绍了mybatisplus中返回Vo的案例,mybatisplus内置的几个方法使用泛型限制了方法的返回类型,所以实现返回Vo还是得自定义方法, 这个方法名尽量不要和原有的名字类似,本文通过实例代码给大家详解讲解,需要的朋友可以参考下
    2023-03-03
  • springboot打印接口调用日志的实例

    springboot打印接口调用日志的实例

    这篇文章主要介绍了springboot打印接口调用日志的实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • IDEA集成Gitee码云的实现步骤

    IDEA集成Gitee码云的实现步骤

    本文主要介绍了IDEA集成Gitee码云的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Linux下启动jar包的几种常见方式

    Linux下启动jar包的几种常见方式

    最近在linux上发布了一个SpringBoot项目,研究了一下jar包在Linux上的集中启动方式,这篇文章主要给大家介绍了Linux下启动jar包的几种常见方式,需要的朋友可以参考下
    2023-07-07
  • Springboot RestTemplate设置超时时间的简单方法

    Springboot RestTemplate设置超时时间的简单方法

    学习springboot ,RestTemplate的使用场景非常非常多,比如springcloud中的服务消费,下面这篇文章主要给大家介绍了关于Springboot RestTemplate设置超时时间的简单方法,需要的朋友可以参考下
    2022-01-01
  • Java中的流式编程问题

    Java中的流式编程问题

    这篇文章主要介绍了Java中的流式编程问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Java实现字符数组全排列的方法

    Java实现字符数组全排列的方法

    这篇文章主要介绍了Java实现字符数组全排列的方法,涉及Java针对字符数组的遍历及排序算法的实现技巧,需要的朋友可以参考下
    2015-12-12

最新评论