java 敏感词检测

     在网上看到好多的敏感词检测,发现都是在推荐某某算法,但是敏感词全是利用文本去存放。在项目中不能很好的进行维护和管理(个人看法)。

    本文的敏感词的检测方式还是DFA算法检测,不过敏感词存放地址放入了Redis的中,方便维护和更新。

1.使用redis进行敏感词存储

将文本中的敏感词读取出来然后存放到redis的中。使用设置类型进行存储(去重)

代码略...

2.使用二叉树进行敏感词分类

跟网上大部分的代码一致(不过本文中是使用单例模式进行调用,只在第一次调用时进行redis读取操作存放到项目中)

package com.cctv.aisys.utils.words;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class BadWordUtil {
	
	public static Set<String> words;
	public static Map<String, String> wordMap;
	public static int minMatchTYpe = 1; // 最小匹配规则
	public static int maxMatchType = 2; // 最大匹配规则
	
	private static BadWordUtil badWordUtil;
	
	private final Logger logger = LoggerFactory.getLogger(BadWordUtil.class);
	
	private BadWordUtil() {}
	/**
	 * 生成敏感词缓存
	 * @param jedisPool
	 */
	private BadWordUtil(JedisPool jedisPool) {
		logger.info("正在生成敏感词缓存");
		BadWordUtil2.words = readTxtByLine(jedisPool);
		addBadWordToHashMap(BadWordUtil2.words);
		logger.info("敏感词缓存成功");
	}
	
	public static BadWordUtil getBadWordUtil(JedisPool jedisPool) {
		if (badWordUtil == null) {
			badWordUtil = new BadWordUtil(jedisPool);
		}
		return badWordUtil;
	}
	
	private Set<String> readTxtByLine(JedisPool jedisPool) {
		Set<String> keyWordSet = new HashSet<String>();
		Jedis jedis = jedisPool.getResource();
		keyWordSet = jedis.smembers("sensitive");
		return keyWordSet;
	}

	/**
	 * 检查文字中是否包含敏感字符,检查规则如下:<br>
	 * 
	 * @param txt
	 * @param beginIndex
	 * @param matchType
	 * @return,如果存在,则返回敏感词字符的长度,不存在返回0
	 * @version 1.0
	 */
	@SuppressWarnings({ "rawtypes" })
	private int checkBadWord(String txt, int beginIndex, int matchType) {
		boolean flag = false; // 敏感词结束标识位:用于敏感词只有1位的情况
		int matchFlag = 0; // 匹配标识数默认为0
		char word = 0;
		Map nowMap = wordMap;
		for (int i = beginIndex; i < txt.length(); i++) {
			word = txt.charAt(i);
			nowMap = (Map) nowMap.get(word); // 获取指定key
			if (nowMap != null) { // 存在,则判断是否为最后一个
				matchFlag++; // 找到相应key,匹配标识+1
				if ("1".equals(nowMap.get("isEnd"))) { // 如果为最后一个匹配规则,结束循环,返回匹配标识数
					flag = true; // 结束标志位为true
					if (minMatchTYpe == matchType) { // 最小规则,直接返回,最大规则还需继续查找
						break;
					}
				}
			} else { // 不存在,直接返回
				break;
			}
		}
		/*
		 * “粉饰”匹配词库:“粉饰太平”竟然说是敏感词 “个人”匹配词库:“个人崇拜”竟然说是敏感词 if(matchFlag < 2 && !flag){
		 * matchFlag = 0; }
		 */
		if (!flag) {
			matchFlag = 0;
		}
		return matchFlag;
	}

	/**
	 * 判断文字是否包含敏感字符
	 * 
	 * @param txt
	 *            文字
	 * @param matchType
	 *            匹配规则 1:最小匹配规则,2:最大匹配规则
	 * @return 若包含返回true,否则返回false
	 * @version 1.0
	 */
	@SuppressWarnings("unused")
	private boolean isContaintBadWord(String txt, int matchType) {
		boolean flag = false;
		for (int i = 0; i < txt.length(); i++) {
			int matchFlag = checkBadWord(txt, i, matchType); // 判断是否包含敏感字符
			if (matchFlag > 0) { // 大于0存在,返回true
				flag = true;
			}
		}
		return flag;
	}

	/**
	 * 替换敏感字字符
	 * 
	 * @param txt
	 * @param matchType
	 * @param replaceChar
	 *            替换字符,默认*
	 * @version 1.0
	 */
	@SuppressWarnings("unused")
	private String replaceBadWord(String txt, int matchType, String replaceChar) {
		String resultTxt = txt;
		Set<String> set = getBadWord(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 txt
	 *            文字
	 * @param matchType
	 *            匹配规则 1:最小匹配规则,2:最大匹配规则
	 * @return
	 * @version 1.0
	 */
	private Set<String> getBadWord(String txt, int matchType) {
		Set<String> sensitiveWordList = new HashSet<String>();

		for (int i = 0; i < txt.length(); i++) {
			int length = checkBadWord(txt, i, matchType); // 判断是否包含敏感字符
			if (length > 0) { // 存在,加入list中
				sensitiveWordList.add(txt.substring(i, i + length));
				i = i + length - 1; // 减1的原因,是因为for会自增
			}
		}

		return sensitiveWordList;
	}

	/**
	 * 获取替换字符串
	 * 
	 * @param replaceChar
	 * @param length
	 * @return
	 * @version 1.0
	 */
	private String getReplaceChars(String replaceChar, int length) {
		String resultReplace = replaceChar;
		for (int i = 1; i < length; i++) {
			resultReplace += replaceChar;
		}

		return resultReplace;
	}

	/**
	 * TODO 将我们的敏感词库构建成了一个类似与一颗一颗的树,这样我们判断一个词是否为敏感词时就大大减少了检索的匹配范围。
	 * 
	 * @param keyWordSet
	 *            敏感词库
	 * @author yqwang0907
	 * @date 2018年2月28日下午5:28:08
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void addBadWordToHashMap(Set<String> keyWordSet) {
		wordMap = new HashMap(keyWordSet.size()); // 初始化敏感词容器,减少扩容操作
		String key = null;
		Map nowMap = null;
		Map<String, String> newWorMap = null;
		// 迭代keyWordSet
		Iterator<String> iterator = keyWordSet.iterator();
		while (iterator.hasNext()) {
			key = iterator.next(); // 关键字
			nowMap = wordMap;
			for (int i = 0; i < key.length(); i++) {
				char keyChar = key.charAt(i); // 转换成char型
				Object wordMap = nowMap.get(keyChar); // 获取

				if (wordMap != null) { // 如果存在该key,直接赋值
					nowMap = (Map) wordMap;
				} else { // 不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个
					newWorMap = new HashMap<String, String>();
					newWorMap.put("isEnd", "0"); // 不是最后一个
					nowMap.put(keyChar, newWorMap);
					nowMap = newWorMap;
				}

				if (i == key.length() - 1) {
					nowMap.put("isEnd", "1"); // 最后一个
				}
			}
		}
	}
	
	/**
	 * 敏感词检测调用接口
	 * @param content 检测文本
	 * @return 检测结果
	 */
	public JSONObject check(String content) {
		JSONObject json = new JSONObject();
		long start = System.currentTimeMillis();
		Set<String> set = BadWordUtil2.getBadWord(content, 5);
		long end = System.currentTimeMillis();
		json.put("keyNum", BadWordUtil2.wordMap.size());
		json.put("result", set);
		json.put("time", end - start);
		boolean flag = true;
		if (set != null && set.size() > 0) {
			flag = false;
		}
		json.put("flag", flag);
		return json;
	}

    /**
	 * 清空敏感词
	 */
	public void clear() {
		badWordUtil = null;
		logger.info("敏感词缓存已清空");
	}

3.输入内容进行敏感词检测

@Test
	public void main1() {
		String string = "一直以为写小说是作家的事,普通如我这样的业余写作爱好者,写小说那只能是梦想。六合彩";
		BadWordUtil badWordUtil = BadWordUtil.getBadWordUtil(jedisPool);
		JSONObject json = badWordUtil.check(string);
		System.out.println(json);
		//输出 :{"result":["六合彩"],"flag":false,"keyNum":1701,"time":1}

4.向redis中添加敏感词并更新程序中的敏感词

 单例模式下,销毁当前对象,然后再生成一个新对象,实现敏感词的更新。

Jedis resource = jedisPool.getResource();
Long sadd = resource.sadd("sensitive", "大乐透七彩");
System.out.println(sadd);
BadWordUtil badWordUtil = BadWordUtil.getBadWordUtil(jedisPool);
badWordUtil.clear();  //单例模式 销毁对象,让他重新new一个对象,实现敏感词的更新
//输出1

24万的敏感词 加载一次只需要4秒。检测时间1-2毫秒。性能是相当的快,而且方便维护与管理

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值