Java实现敏感词过滤实例

 更新时间:2016年11月19日 14:30:57   作者:chenssy  
敏感词、文字过滤是一个网站必不可少的功能,本篇文章主要介绍了Java实现敏感词过滤实例,具有一定的参考价值,有需要的可以了解一下。

敏感词、文字过滤是一个网站必不可少的功能,如何设计一个好的、高效的过滤算法是非常有必要的。前段时间我一个朋友(马上毕业,接触编程不久)要我帮他看一个文字过滤的东西,它说检索效率非常慢。我把它程序拿过来一看,整个过程如下:读取敏感词库、如果HashSet集合中,获取页面上传文字,然后进行匹配。我就想这个过程肯定是非常慢的。对于他这个没有接触的人来说我想也只能想到这个,更高级点就是正则表达式。但是非常遗憾,这两种方法都是不可行的。当然,在我意识里没有我也没有认知到那个算法可以解决问题,但是Google知道!

 DFA简介

在实现文字过滤的算法中,DFA是唯一比较好的实现算法。DFA即Deterministic Finite Automaton,也就是确定有穷自动机,它是是通过event和当前的state得到下一个state,即event+state=nextstate。下图展示了其状态的转换
在这幅图中大写字母(S、U、V、Q)都是状态,小写字母a、b为动作。通过上图我们可以看到如下关系

a b b

S -----> U S -----> V U -----> V

 在实现敏感词过滤的算法中,我们必须要减少运算,而DFA在DFA算法中几乎没有什么计算,有的只是状态的转换。

Java实现DFA算法实现敏感词过滤

在Java中实现敏感词过滤的关键就是DFA算法的实现。首先我们对上图进行剖析。在这过程中我们认为下面这种结构会更加清晰明了。



同时这里没有状态转换,没有动作,有的只是Query(查找)。我们可以认为,通过S query U、V,通过U query V、P,通过V query U P。通过这样的转变我们可以将状态的转换转变为使用Java集合的查找。

诚然,加入在我们的敏感词库中存在如下几个敏感词:日本人、日本鬼子、毛.泽.东。那么我需要构建成一个什么样的结构呢?

首先:query 日 ---> {本}、query 本 --->{人、鬼子}、query 人 --->{null}、query 鬼 ---> {子}。形如下结构:

下面我们在对这图进行扩展:


这样我们就将我们的敏感词库构建成了一个类似与一颗一颗的树,这样我们判断一个词是否为敏感词时就大大减少了检索的匹配范围。比如我们要判断日本人,根据第一个字我们就可以确认需要检索的是那棵树,然后再在这棵树中进行检索。

但是如何来判断一个敏感词已经结束了呢?利用标识位来判断。

所以对于这个关键是如何来构建一棵棵这样的敏感词树。下面我已Java中的HashMap为例来实现DFA算法。具体过程如下:

日本人,日本鬼子为例

1、在hashMap中查询“日”看其是否在hashMap中存在,如果不存在,则证明已“日”开头的敏感词还不存在,则我们直接构建这样的一棵树。跳至3。

2、如果在hashMap中查找到了,表明存在以“日”开头的敏感词,设置hashMap = hashMap.get("日"),跳至1,依次匹配“本”、“人”。

 3、判断该字是否为该词中的最后一个字。若是表示敏感词结束,设置标志位isEnd = 1,否则设置标志位isEnd = 0;

  程序实现如下:

 /** 
  * 读取敏感词库,将敏感词放入HashSet中,构建一个DFA算法模型:<br> 
  * 中 = { 
  *  isEnd = 0 
  *  国 = {<br> 
  *   isEnd = 1 
  *   人 = {isEnd = 0 
  *    民 = {isEnd = 1} 
  *    } 
  *   男 = { 
  *     isEnd = 0 
  *     人 = { 
  *      isEnd = 1 
  *      } 
  *    } [ 0-9]
  *   } 
  *  } 
  * 五 = { 
  *  isEnd = 0 
  *  星 = { 
  *   isEnd = 0 
  *   红 = { 
  *    isEnd = 0 
  *    旗 = { 
  *     isEnd = 1 
  *     } 
  *    } 
  *   } 
  *  } 
  * @author chenming 
  * @date 2014年4月20日 下午3:04:20 
  * @param keyWordSet 敏感词库 
  * @version 0 
  */ 
 @SuppressWarnings({ "rawtypes", "unchecked" }) 
 private void addSensitiveWordToHashMap(Set<String> keyWordSet) { 
  sensitiveWordMap = new HashMap(keyWordSetsize());  //初始化敏感词容器,减少扩容操作 
  String key = null; 
  Map nowMap = null; 
  Map<String, String> newWorMap = null; 
  //迭代keyWordSet 
  Iterator<String> iterator = keyWordSetiterator(); 
  while(iteratorhasNext()){ 
   key = iteratornext(); //关键字 
   nowMap = sensitiveWordMap; 
   for(int i = 0 ; i < keylength() ; i++){ 
    char keyChar = keycharAt(i);  //转换成char型 
    Object wordMap = nowMapget(keyChar);  //获取 
     
    if(wordMap != null){  //如果存在该key,直接赋值 
     nowMap = (Map) wordMap; 
    } 
    else{  //不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个 
     newWorMap = new HashMap<String,String>(); 
     newWorMapput("isEnd", "0");  //不是最后一个 
     nowMapput(keyChar, newWorMap); 
     nowMap = newWorMap; 
    } 
     
    if(i == keylength() - 1){ 
     nowMapput("isEnd", "1"); //最后一个 
    } 
   } 
  } 
 } 

运行得到的hashMap结构如下:

{五={星={红={isEnd=0, 旗={isEnd=1}}, isEnd=0}, isEnd=0}, 中={isEnd=0, 国={isEnd=0, 人={isEnd=1}, 男={isEnd=0, 人={isEnd=1}}}}}

敏感词库我们一个简单的方法给实现了,那么如何实现检索呢?检索过程无非就是hashMap的get实现,找到就证明该词为敏感词,否则不为敏感词。过程如下:假如我们匹配“中国人民万岁”。

 1、第一个字“中”,我们在hashMap中可以找到。得到一个新的map = hashMap.get("")。

2、如果map == null,则不是敏感词。否则跳至3

3、获取map中的isEnd,通过isEnd是否等于1来判断该词是否为最后一个。如果isEnd == 1表示该词为敏感词,否则跳至1。

通过这个步骤我们可以判断“中国人民”为敏感词,但是如果我们输入“中国女人”则不是敏感词了。

/** 
  * 检查文字中是否包含敏感字符,检查规则如下:<br> 
  * @author chenming 
  * @date 2014年4月20日 下午4:31:03 
  * @param txt 
  * @param beginIndex 
  * @param matchType 
  * @return,如果存在,则返回敏感词字符的长度,不存在返回0 
  * @version 0 
  */ 
 @SuppressWarnings({ "rawtypes"}) 
 public int CheckSensitiveWord(String txt,int beginIndex,int matchType){ 
  boolean flag = false; //敏感词结束标识位:用于敏感词只有1位的情况 
  int matchFlag = 0;  //匹配标识数默认为0 
  char word = 0; 
  Map nowMap = sensitiveWordMap; 
  for(int i = beginIndex; i < txtlength() ; i++){ 
   word = txtcharAt(i); 
   nowMap = (Map) nowMapget(word);  //获取指定key 
   if(nowMap != null){  //存在,则判断是否为最后一个 
    matchFlag++;  //找到相应key,匹配标识+1 
    if("1"equals(nowMapget("isEnd"))){  //如果为最后一个匹配规则,结束循环,返回匹配标识数 
     flag = true;  //结束标志位为true  
     if(SensitivewordFilterminMatchTYpe == matchType){ //最小规则,直接返回,最大规则还需继续查找 
      break; 
     } 
    } 
   } 
   else{  //不存在,直接返回 
    break; 
   } 
  } 
  if(matchFlag < 2 && !flag){  
   matchFlag = 0; 
  } 
  return matchFlag; 
 } 

在文章末尾我提供了利用Java实现敏感词过滤的文件下载。下面是一个测试类来证明这个算法的效率和可靠性。

public static void main(String[] args) { 
  SensitivewordFilter filter = new SensitivewordFilter(); 
  Systemoutprintln("敏感词的数量:" + filtersensitiveWordMapsize()); 
  String string = "太多的伤感情怀也许只局限于饲养基地 荧幕中的情节,主人公尝试着去用某种方式渐渐的很潇洒地释自杀指南怀那些自己经历的伤感。" 
      + "然后法轮功 我们的扮演的角色就是跟随着主人公的喜红客联盟 怒哀乐而过于牵强的把自己的情感也附加于银幕情节中,然后感动就流泪," 
      + "难过就躺在某一个人的怀里尽情的阐述心扉或者手机卡复制器一个人一杯红酒一部电影在夜三级片 深人静的晚上,关上电话静静的发呆着。"; 
  Systemoutprintln("待检测语句字数:" + stringlength()); 
  long beginTime = SystemcurrentTimeMillis(); 
  Set<String> set = filtergetSensitiveWord(string, 1); 
  long endTime = SystemcurrentTimeMillis(); 
  Systemoutprintln("语句中包含敏感词的个数为:" + setsize() + "。包含:" + set); 
  Systemoutprintln("总共消耗时间为:" + (endTime - beginTime)); 
 } 

运行结果:

 从上面的结果可以看出,敏感词库有771个,检测语句长度为184个字符,查出6个敏感词。总共耗时1毫秒。可见速度还是非常可观的。

下面提供两个文档下载:

Desktop.rar(http://xiazai.jb51.net/201611/yuanma/Desktop_jb51.rar)里面包含两个Java文件,一个是读取敏感词库(SensitiveWordInit),一个是敏感词工具类(SensitivewordFilter),里面包含了判断是否存在敏感词(isContaintSensitiveWord(String txt,int matchType))、获取敏感词(getSensitiveWord(String txt , int matchType))、敏感词替代(replaceSensitiveWord(String txt,int matchType,String replaceChar))三个方法。

敏感词库:点击下载

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • springboot整合logback打印日志,分文件

    springboot整合logback打印日志,分文件

    本文主要介绍了springboot整合logback打印日志,分文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-11-11
  • 深入探究MyBatis插件机制灵活扩展及自定义增强框架能力

    深入探究MyBatis插件机制灵活扩展及自定义增强框架能力

    这篇文章主要介绍了深入探究MyBatis插件机制灵活扩展及自定义增强框架能力
    2024-01-01
  • java设计模式之策略模式在促销活动场景中的使用案例

    java设计模式之策略模式在促销活动场景中的使用案例

    这篇文章主要为大家介绍了java设计模式之策略模式在促销活动场景中案例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Maven设置使用自定义的jar包到自己本地仓库

    Maven设置使用自定义的jar包到自己本地仓库

    今天小编就为大家分享一篇关于Maven设置使用自定义的jar包到自己本地仓库的文章,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • springboot 事件监听器的案例详解

    springboot 事件监听器的案例详解

    这篇文章主要介绍了springboot 事件监听器,springboot(spring)的事件监听器使用主要有两种方式,通过实现ApplicationListener接口,另一个就是在类上添加 @EventListener 注解来实现,接下来将对这两种方式逐一说明,需要的朋友可以参考下
    2022-06-06
  • Java轻松掌握面向对象的三大特性封装与继承和多态

    Java轻松掌握面向对象的三大特性封装与继承和多态

    本文主要讲述的是面向对象的三大特性:封装,继承,多态,内容含括从封装到继承再到多态的所有重点内容以及使用细节和注意事项,内容有点长,请大家耐心看完
    2022-05-05
  • 聊一聊Java反射

    聊一聊Java反射

    工作中哪些地方比较容易用到反射,这篇文章就为大家介绍了工作中常用到的Java反射,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • Java字符串数字左右补全0的四种方式

    Java字符串数字左右补全0的四种方式

    这篇文章主要给大家介绍了关于Java字符串数字左右补全0的四种方式,在编程中有时候我们需要对一个字符串进行字符填充,以满足某些特定的要求,其中补全0是一种常见的需求,需要的朋友可以参考下
    2023-08-08
  • 如何测试Java类的线程安全性

    如何测试Java类的线程安全性

    这篇文章主要介绍了如何测试Java类的线程安全性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 使用Java Servlet生成动态二维码的实现步骤

    使用Java Servlet生成动态二维码的实现步骤

    在现代互联网时代,二维码广泛应用于各个领域,包括支付、认证、信息传递等,在Web开发中,通过Java Servlet生成动态二维码是一个常见的需求,本文将介绍如何使用Java Servlet结合Google的ZXing库生成动态二维码,需要的朋友可以参考下
    2023-11-11

最新评论