Java实现DFA算法对敏感词、广告词过滤功能示例

一、前言

开发中经常要处理用户一些文字的提交,所以涉及到了敏感词过滤的功能,参考资料中DFA有穷状态机算法的实现,创建有向图。完成了对敏感词、广告词的过滤,而且效率较好,所以分享一下。

具体实现:

1、匹配大小写过滤
 2、匹配全角半角过滤
 3、匹配过滤停顿词过滤。
 4、敏感词重复词过滤。

例如:

支持如下类型类型过滤检测:

fuck 全小写

FuCk 大小写

fuck全角半角

f!!!u&c ###k 停顿词

fffuuuucccckkk 重复词

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

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

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

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

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

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

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

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

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

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

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

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

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

二、代码实现

其目录结构如下:

其中resources资源目录中:

stopwd.txt :停顿词,匹配时间直接过滤。

wd.txt:敏感词库。

1、WordFilter敏感词过滤类

package org.andy.sensitivewdfilter; 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set; 

import org.andy.sensitivewdfilter.util.BCConvert; 

/**
 * 创建时间:2016年8月30日 下午3:01:12
 *
 * 思路: 创建一个FilterSet,枚举了0~65535的所有char是否是某个敏感词开头的状态
 *
 * 判断是否是 敏感词开头 | | 是 不是 获取头节点 OK--下一个字 然后逐级遍历,DFA算法
 *
 * @author andy
 * @version 2.2
 */
public class WordFilter { 

  private static final FilterSet set = new FilterSet(); // 存储首字
  private static final Map<Integer, WordNode> nodes = new HashMap<Integer, WordNode>(1024, 1); // 存储节点
  private static final Set<Integer> stopwdSet = new HashSet<>(); // 停顿词
  private static final char SIGN = '*'; // 敏感词过滤替换 

  static {
    try {
      long a = System.nanoTime();
      init();
      a = System.nanoTime() - a;
      System.out.println("加载时间 : " + a + "ns");
      System.out.println("加载时间 : " + a / 1000000 + "ms");
    } catch (Exception e) {
      throw new RuntimeException("初始化过滤器失败");
    }
  } 

  private static void init() {
    // 获取敏感词
    addSensitiveWord(readWordFromFile("wd.txt"));
    addStopWord(readWordFromFile("stopwd.txt"));
  } 

  /**
   * 增加敏感词
   * @param path
   * @return
   */
  private static List<String> readWordFromFile(String path) {
    List<String> words;
    BufferedReader br = null;
    try {
      br = new BufferedReader(new InputStreamReader(WordFilter.class.getClassLoader().getResourceAsStream(path)));
      words = new ArrayList<String>(1200);
      for (String buf = ""; (buf = br.readLine()) != null;) {
        if (buf == null || buf.trim().equals(""))
          continue;
        words.add(buf);
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      try {
        if (br != null)
          br.close();
      } catch (IOException e) {
      }
    }
    return words;
  } 

  /**
   * 增加停顿词
   *
   * @param words
   */
  private static void addStopWord(final List<String> words) {
    if (words != null && words.size() > 0) {
      char[] chs;
      for (String curr : words) {
        chs = curr.toCharArray();
        for (char c : chs) {
          stopwdSet.add(charConvert(c));
        }
      }
    }
  } 

  /**
   * 添加DFA节点
   * @param words
   */
  private static void addSensitiveWord(final List<String> words) {
    if (words != null && words.size() > 0) {
      char[] chs;
      int fchar;
      int lastIndex;
      WordNode fnode; // 首字母节点
      for (String curr : words) {
        chs = curr.toCharArray();
        fchar = charConvert(chs[0]);
        if (!set.contains(fchar)) {// 没有首字定义
          set.add(fchar);// 首字标志位 可重复add,反正判断了,不重复了
          fnode = new WordNode(fchar, chs.length == 1);
          nodes.put(fchar, fnode);
        } else {
          fnode = nodes.get(fchar);
          if (!fnode.isLast() && chs.length == 1)
            fnode.setLast(true);
        }
        lastIndex = chs.length - 1;
        for (int i = 1; i < chs.length; i++) {
          fnode = fnode.addIfNoExist(charConvert(chs[i]), i == lastIndex);
        }
      }
    }
  } 

  /**
   * 过滤判断 将敏感词转化为成屏蔽词
   * @param src
   * @return
   */
  public static final String doFilter(final String src) {
    char[] chs = src.toCharArray();
    int length = chs.length;
    int currc;
    int k;
    WordNode node;
    for (int i = 0; i < length; i++) {
      currc = charConvert(chs[i]);
      if (!set.contains(currc)) {
        continue;
      }
      node = nodes.get(currc);// 日 2
      if (node == null)// 其实不会发生,习惯性写上了
        continue;
      boolean couldMark = false;
      int markNum = -1;
      if (node.isLast()) {// 单字匹配(日)
        couldMark = true;
        markNum = 0;
      }
      // 继续匹配(日你/日你妹),以长的优先
      // 你-3 妹-4 夫-5
      k = i;
      for (; ++k < length;) {
        int temp = charConvert(chs[k]);
        if (stopwdSet.contains(temp))
          continue;
        node = node.querySub(temp);
        if (node == null)// 没有了
          break;
        if (node.isLast()) {
          couldMark = true;
          markNum = k - i;// 3-2
        }
      }
      if (couldMark) {
        for (k = 0; k <= markNum; k++) {
          chs[k + i] = SIGN;
        }
        i = i + markNum;
      }
    } 

    return new String(chs);
  } 

  /**
   * 是否包含敏感词
   * @param src
   * @return
   */
  public static final boolean isContains(final String src) {
    char[] chs = src.toCharArray();
    int length = chs.length;
    int currc;
    int k;
    WordNode node;
    for (int i = 0; i < length; i++) {
      currc = charConvert(chs[i]);
      if (!set.contains(currc)) {
        continue;
      }
      node = nodes.get(currc);// 日 2
      if (node == null)// 其实不会发生,习惯性写上了
        continue;
      boolean couldMark = false;
      if (node.isLast()) {// 单字匹配(日)
        couldMark = true;
      }
      // 继续匹配(日你/日你妹),以长的优先
      // 你-3 妹-4 夫-5
      k = i;
      for (; ++k < length;) {
        int temp = charConvert(chs[k]);
        if (stopwdSet.contains(temp))
          continue;
        node = node.querySub(temp);
        if (node == null)// 没有了
          break;
        if (node.isLast()) {
          couldMark = true;
        }
      }
      if (couldMark) {
        return true;
      }
    } 

    return false;
  } 

  /**
   * 大写转化为小写 全角转化为半角
   *
   * @param src
   * @return
   */
  private static int charConvert(char src) {
    int r = BCConvert.qj2bj(src);
    return (r >= 'A' && r <= 'Z') ? r + 32 : r;
  } 

}

其中:

isContains :是否包含敏感词
doFilter:过滤敏感词

2、WordNode敏感词节点

package org.andy.sensitivewdfilter; 

import java.util.LinkedList;
import java.util.List; 

/**
 * 创建时间:2016年8月30日 下午3:07:45
 *
 * @author andy
 * @version 2.2
 */
public class WordNode { 

  private int value; // 节点名称 

  private List<WordNode> subNodes; // 子节点 

  private boolean isLast;// 默认false 

  public WordNode(int value) {
    this.value = value;
  } 

  public WordNode(int value, boolean isLast) {
    this.value = value;
    this.isLast = isLast;
  } 

  /**
   *
   * @param subNode
   * @return 就是传入的subNode
   */
  private WordNode addSubNode(final WordNode subNode) {
    if (subNodes == null)
      subNodes = new LinkedList<WordNode>();
    subNodes.add(subNode);
    return subNode;
  } 

  /**
   * 有就直接返回该子节点, 没有就创建添加并返回该子节点
   *
   * @param value
   * @return
   */
  public WordNode addIfNoExist(final int value, final boolean isLast) {
    if (subNodes == null) {
      return addSubNode(new WordNode(value, isLast));
    }
    for (WordNode subNode : subNodes) {
      if (subNode.value == value) {
        if (!subNode.isLast && isLast)
          subNode.isLast = true;
        return subNode;
      }
    }
    return addSubNode(new WordNode(value, isLast));
  } 

  public WordNode querySub(final int value) {
    if (subNodes == null) {
      return null;
    }
    for (WordNode subNode : subNodes) {
      if (subNode.value == value)
        return subNode;
    }
    return null;
  } 

  public boolean isLast() {
    return isLast;
  } 

  public void setLast(boolean isLast) {
    this.isLast = isLast;
  } 

  @Override
  public int hashCode() {
    return value;
  } 

}

三、测试结果

项目包含敏感词库,源码,停顿词库等,只需运行maven打jar包直接可运行。

项目源码:sensitivewd-filter_jb51.rar

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

(0)

相关推荐

  • Java基于分治算法实现的棋盘覆盖问题示例

    本文实例讲述了Java基于分治算法实现的棋盘覆盖问题.分享给大家供大家参考,具体如下: 在一个2^k * 2^k个方格组成的棋盘中,有一个方格与其它的不同,若使用以下四种L型骨牌覆盖除这个特殊方格的其它方格,如何覆盖.四个L型骨牌如下图: 棋盘中的特殊方格如图: 实现的基本原理是将2^k * 2^k的棋盘分成四块2^(k - 1) * 2^(k - 1)的子棋盘,特殊方格一定在其中的一个子棋盘中,如果特殊方格在某一个子棋盘中,继续递归处理这个子棋盘,直到这个子棋盘中只有一个方格为止如果特殊方格不

  • 基于Java实现的图的广度优先遍历算法

    本文以实例形式讲述了基于Java的图的广度优先遍历算法实现方法,具体方法如下: 用邻接矩阵存储图方法: 1.确定图的顶点个数和边的个数 2.输入顶点信息存储在一维数组vertex中 3.初始化邻接矩阵: 4.依次输入每条边存储在邻接矩阵arc中 输入边依附的两个顶点的序号i,j: 将邻接矩阵的第i行第j列的元素值置为1: 将邻接矩阵的第j行第i列的元素值置为1: 广度优先遍历实现: 1.初始化队列Q 2.访问顶点v:visited[v]=1;顶点v入队Q; 3.while(队列Q非空) v=队列

  • Java基于递归和循环两种方式实现未知维度集合的笛卡尔积算法示例

    本文实例讲述了Java基于递归和循环两种方式实现未知维度集合的笛卡尔积.分享给大家供大家参考,具体如下: 什么是笛卡尔积? 在数学中,两个集合X和Y的笛卡儿积(Cartesian product),又称直积,表示为X × Y,第一个对象是X的成员而第二个对象是Y的所有可能有序对的其中一个成员. 假设集合A={a,b},集合B={0,1,2},则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1), (b,2)}. 如何用程序算法实现笛卡尔积? 如果编程前已知集合的数量

  • Java矩阵连乘问题(动态规划)算法实例分析

    本文实例讲述了Java矩阵连乘问题(动态规划)算法.分享给大家供大家参考,具体如下: 问题描述:给定n个矩阵:A1,A2,...,An,其中Ai与Ai+1是可乘的,i=1,2...,n-1.确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少.输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数. 问题解析:由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序.这种计算次序可以用加括号的方式来确定.若一个矩阵连乘积的计算次序完全确

  • Java语言实现Blowfish加密算法完整代码分享

    前几天网上突然出现流言:某东发生数据泄露12G,最终某东在一篇声明中没有否认,还算是勉强承认了吧,这件事对于一般人有什么影响.应该怎么做已经有一堆人说了,所以就不凑热闹了,咱来点对程序猿来说实际点的,说一个个人认为目前比较安全的加密算法:Blowfish. 上代码之前,先说几点Blowfish加密算法的特点: 1. 对称加密,即加密的密钥和解密的密钥是相同的: 2. 每次加密之后的结果是不同的(这也是老夫比较欣赏的一点): 3. 可逆的,和老夫之前的文章介绍的md5等摘要算法不一样,他是可逆的:

  • Java编程实现A*算法完整代码

    前言 A*搜寻算法俗称A星算法.这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法.常用于游戏中 通过二维数组构建的一个迷宫,"%"表示墙壁,A为起点,B为终点,"#"代表障碍物,"*"代表算法计算后的路径 本文实例代码结构: % % % % % % % % o o o o o % % o o # o o % % A o # o B % % o o # o o % % o o o o o % % % % % % % % =======

  • 70行Java代码实现深度神经网络算法分享

    对于现在流行的深度学习,保持学习精神是必要的--程序员尤其是架构师永远都要对核心技术和关键算法保持关注和敏感,必要时要动手写一写掌握下来,先不用关心什么时候用到--用不用是政治问题,会不会写是技术问题,就像军人不关心打不打的问题,而要关心如何打赢的问题. 程序员如何学习机器学习 对程序员来说,机器学习是有一定门槛的(这个门槛也是其核心竞争力),相信很多人在学习机器学习时都会为满是数学公式的英文论文而头疼,甚至可能知难而退.但实际上机器学习算法落地程序并不难写,下面是70行代码实现的反向多层(BP

  • Java基于栈方式解决汉诺塔问题实例【递归与非递归算法】

    本文实例讲述了Java基于栈方式解决汉诺塔问题.分享给大家供大家参考,具体如下: /** * 栈方式非递归汉诺塔 * @author zy * */ public class StackHanoi { /** * @param args */ public static void main(String[] args) { System.out.println("我们测试结果:"); System.out.println("递归方式:"); hanoiNormal(

  • Java语言实现快速幂取模算法详解

    快速幂取模算法的引入是从大数的小数取模的朴素算法的局限性所提出的,在朴素的方法中我们计算一个数比如5^1003%31是非常消耗我们的计算资源的,在整个计算过程中最麻烦的就是我们的5^1003这个过程 缺点1:在我们在之后计算指数的过程中,计算的数字不都拿得增大,非常的占用我们的计算资源(主要是时间,还有空间) 缺点2:我们计算的中间过程数字大的恐怖,我们现有的计算机是没有办法记录这么长的数据的,所以说我们必须要想一个更加高效的方法来解决这个问题 当我们计算AB%C的时候,最便捷的方法就是调用Ma

  • 基于Java实现的一层简单人工神经网络算法示例

    本文实例讲述了基于Java实现的一层简单人工神经网络算法.分享给大家供大家参考,具体如下: 先来看看笔者绘制的算法图: 2.数据类 import java.util.Arrays; public class Data { double[] vector; int dimention; int type; public double[] getVector() { return vector; } public void setVector(double[] vector) { this.vect

  • 关于JAVA经典算法40题(超实用版)

    [程序1]题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第四个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?1.程序分析: 兔子的规律为数列1,1,2,3,5,8,13,21....public class exp2{ public static void main(String args[]){ int i=0; for(i=1;i<=20;i++)System.out.println(f(i));}public static int f(in

随机推荐