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

原创 2016年08月31日 11:47:35

一、前言

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

具体实现:

 1、匹配大小写过滤

 2、匹配全角半角过滤

 3、匹配过滤停顿词过滤。

 4、敏感词重复词过滤。


例如:

支持如下类型类型过滤检测:
fuck 全小写
FuCk 大小写
fuck全角半角
f!!!u&c ###k 停顿词
fffuuuucccckkk 重复词

二、代码实现

    其目录结构如下:


   其中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包直接可运行。



博客来源http://blog.csdn.net/fengshizty?viewmode=list

项目源码http://download.csdn.net/detail/fengshizty/9617695



版权声明:本文为博主原创文章,未经博主允许不得转载。

Java实现敏感词过滤

敏感词、文字过滤是一个网站必不可少的功能,如何设计一个好的、高效的过滤算法是非常有必要的。前段时间我一个朋友(马上毕业,接触编程不久)要我帮他看一个文字过滤的东西,它说检索效率非常慢。我把它程序拿过来...
  • chenssy
  • chenssy
  • 2014年05月25日 15:42
  • 83635

JavaWeb-过滤器Filter学习(四)敏感词过滤实例

通过Filter来实现留言板的敏感词过滤…思路很简单,我们这里的敏感词是直接先放进去的,实际项目中,肯定是存在数据库中。在Filter 过滤器中,我们先拿到用户提交的留言,如果出现了敏感词,我们就用*...

敏感词过滤与DFA算法Trie树

关键字过滤 trie树

高效Java敏感词、关键词过滤工具包_过滤非法词句

使用说明:1、本工具包由北京师范大学计算机系 张人杰 开发制作 基于多叉树的查找,任何问题请联系:alex.zhangrj@hotmail.com2、工具包自带敏感词词库,第一次调用时读入词库,故第一...
  • ranjio_z
  • ranjio_z
  • 2011年04月03日 12:18
  • 16764

[Java Web]敏感词过滤算法

1.DFA算法 DFA算法的原理可以参考 这里 ,简单来说就是通过Map构造出一颗敏感词树,树的每一条由根节点到叶子节点的路径构成一个敏感词,例如下图: 代码简单实现如下: pu...

Java实现敏感词过滤 - IKAnalyzer中文分词工具

IKAnalyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包。官网: https://code.google.com/archive/p/ik-analyzer/本用例借助 IKA...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

java实现敏感词过滤

java实现敏感词过滤 例如敏感词为:日本人、日本鬼子、毛泽东 构建一种树形结构如下图: 这样我们就将敏感词库构建成了一个类似于一颗一颗的树,我们判断一个词是否为敏感词时就大大减少了...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Java实现敏感词过滤

1.读取敏感数据,构建DFA算法 package com.iris.nsonline.nsoweb.utils; import java.io.BufferedReader; import java...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java实现DFA算法对敏感词、广告词过滤功能
举报原因:
原因补充:

(最多只允许输入30个字)