Java过滤敏感词汇

作者:じ☆ve宝贝

发布时间:2016-06-12T17:29:57

上午和朋友聊天说到敏感词这一块,刚好公司项目有一个评论功能,还没有写敏感词过滤,就修改一个敏感词过滤的工具类。希望可以帮助到大家

package cn.studyjava.utils; 

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

public class TextFilterUtil {

    //日志
    private static final Logger LOG = Logger.getLogger(TextFilterUtil.class);
    //敏感词库
    @SuppressWarnings("rawtypes")
	private static HashMap sensitiveWordMap = null;
    //默认编码格式
    private static final String ENCODING = "UTF-8";
    //敏感词库的路径
    private static final InputStream in = TextFilterUtil.class.getClassLoader().getResourceAsStream("cn/studyjava/utils/keyWords.txt");

    /**
     * 初始化敏感词库
     */
    private static void init() {
        //读取文件
        Set<String> keyWords = readSensitiveWords();
        //创建敏感词库
        sensitiveWordMap = new HashMap<>(keyWords.size());
        for (String keyWord : keyWords) {
            createKeyWord(keyWord);
        }
    }

    /**
     * 构建敏感词库,将敏感词放入HashSet中,构建一个DFA算法模型:<br>
	 * 中 = {
	 *      isEnd = 0
	 *      国 = {<br>
	 *      	 isEnd = 1
	 *           人 = {isEnd = 0
	 *                民 = {isEnd = 1}
	 *                }
	 *           男  = {
	 *           	   isEnd = 0
	 *           		人 = {
	 *           			 isEnd = 1
	 *           			}
	 *           	}
	 *           }
	 *      }
	 *  五 = {
	 *      isEnd = 0
	 *      星 = {
	 *      	isEnd = 0
	 *      	红 = {
	 *              isEnd = 0
	 *              旗 = {
	 *                   isEnd = 1
	 *                  }
	 *              }
	 *      	}
	 *      }
     *
     * @param keyWord
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
	private static void createKeyWord(String keyWord) {
        if (sensitiveWordMap == null) {
            LOG.error("sensitiveWordMap 未初始化!");
            return;
        }
        Map<Object, Object> nowMap = sensitiveWordMap;
        for (Character c : keyWord.toCharArray()) {
            Object obj = nowMap.get(c);
            if (obj == null) {
                Map<Object, Object> childMap = new HashMap<>();
                childMap.put("isEnd", "false");
                nowMap.put(c, childMap);
                nowMap = childMap;
            } else {
                nowMap = (Map) obj;
            }
        }
        nowMap.put("isEnd", "true");
    }

    /**
     * 读取敏感词文件
     *
     * @return
     */
    private static Set<String> readSensitiveWords() {
        Set<String> keyWords = new HashSet<>();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(in, ENCODING));
            String line;
            while ((line = reader.readLine()) != null) {
                keyWords.add(line.trim());
            }
        } catch (UnsupportedEncodingException e) {
            LOG.error("敏感词库文件转码失败!");
        } catch (FileNotFoundException e) {
            LOG.error("敏感词库文件不存在!");
        } catch (IOException e) {
            LOG.error("敏感词库文件读取失败!");
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                reader = null;
            }
        }
        return keyWords;
    }

    /**
     * 检查敏感词
     *
     * @return
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
	private static Set<String> checkSensitiveWord(String text) {
        if (sensitiveWordMap == null) {
            init();
        }
        Set<String> sensitiveWords = new HashSet<String>();
        Map<Object,Object> nowMap = sensitiveWordMap;
       
        for (int i = 0; i < text.length(); i++) {
            Character word = text.charAt(i);
            Object obj = nowMap.get(word);
            if (obj == null) {
                continue;
            }
            int j = i + 1;
            boolean flag = false;
            Map<Object,Object> childMap = (Map) obj;
            int matchFlag = 1;     //匹配标识数默认为1,其实就是匹配的关键词的长度
            while (j < text.length()) {
            	obj = childMap.get(text.charAt(j));
                if (obj != null) {
                    childMap = (Map) obj;
                    matchFlag++;
                    if ("true".equals(childMap.get("isEnd"))) { // 判断是否为最后一个匹配
                    	flag = true;
                    }
                } else {
                    break;
                }
                j++;
            }
            if(matchFlag > 1 && flag){
            	sensitiveWords.add(text.substring(i, i+matchFlag)); // 把敏感词添加的集合中
            	i = i + matchFlag; // 添加完成敏感词后应该跳过本次添加的长度防止 出现例如将:法轮功、轮功这种情况可以避免出现两次 
            }
            
        }
        return sensitiveWords;
    }
    
    /**
     * 
     * 作者: zsljava
     * 邮件: zsljava@163.com 
     * @param txt 检测的文本
     * @param replaceChar 敏感词的替换字符
     * @return 
     * 时间: 2016年6月12日 下午5:13:22
     * 描述:
     */
    public static String replaceSensitiveWord(String txt,String replaceChar){
    	String resultTxt = txt;
		String replaceString = null;
    	Set<String> sensitiveWords = checkSensitiveWord(txt);
    	for (String word : sensitiveWords) {
    		replaceString = getReplaceChars(replaceChar,word.length());
    		resultTxt = resultTxt.replaceAll(word, replaceString);
		}
    	return resultTxt;
    }
    
    /**
     * 
     * 作者: zsljava
     * 邮件: zsljava@163.com 
     * @param replaceChar 替换的字符
     * @param length 长度
     * @return 
     * 时间: 2016年6月12日 下午5:12:02
     * 描述: 一般用 * 多少字就有多少 * 
     */
	private static String getReplaceChars(String replaceChar,int length){ 
		String resultReplace = replaceChar;
		for(int i = 1 ; i < length ; i++){
			resultReplace += replaceChar;
		}
		
		return resultReplace;
	}
    
    public static void main(String[] args) {
		init();
    	String string = "关于一些事情,我有必要解释一下."
    			+ "最近5个院系又搞联合晚会了,鉴于和生科院的特殊关系,我拒绝邀请参加演出.因此惹来几句\"耍大牌\"云云."
    			+ "首先,感谢您授予我大牌这个称呼,我真的受精若宠,"
    			+ "二来,这个和大牌贴不上任何毛关系,只是本人私人恩怨而已,"
    			+ "第三,我对于生科院的帮助也不是没有,一个校级迎新晚会生科院应该挺风光的了,被我反复强调我是生科院,况且,生科院宣传板还是我花半天一晚,一边因为下不去楼和女朋友吵架一边做好的,换了别人谁做得出!"
    			+ "第四,和桂乐山有关的一切演出我绝不会推卸,所以别说场子小或者小演出\"委屈我\"之类羞臊我,最重要一条,我不喜欢干她更不喜欢天天拿干她当头等大事的院!"
    			+ "我爷爷年轻的时候是一个镇长,就因为私下接济了一个穷人给他假钞,"
    			+ "在然后法轮功我们的扮演的角色就是跟随着主人公的喜红客联盟 怒哀乐而过于牵强的把自己的情感也附加于银幕情节中,然后感动就流泪,"
				+ "难过就躺在某一个人的怀里尽情的阐述心扉或者手机卡复制器一个人一杯红酒一部电影在夜三级片 深人静的晚上,关上电话静静的发呆着。无界";
    	
		System.out.println("------------------------------------------------------------------------------------");
		System.out.println("敏感词的数量:" + sensitiveWordMap.size());
		System.out.println("待检测语句字数:" + string.length());
		long beginTime = System.currentTimeMillis();
		Set<String> sensitiveWords = checkSensitiveWord(string);
		System.out.println("语句中包含敏感词的个数为:" + sensitiveWords.size() + "。包含:" + sensitiveWords);
		long endTime = System.currentTimeMillis();
		System.out.println("总共消耗时间为:" + (endTime - beginTime)+"/毫秒");
		beginTime = System.currentTimeMillis();
		System.out.println(replaceSensitiveWord(string,"*"));
		endTime = System.currentTimeMillis();
		System.out.println("替换敏感词总共消耗时间为:" + (endTime - beginTime)+"/毫秒");
		System.out.println("------------------------------------------------------------------------------------");
    	
//		String s= "法轮功";
//		System.out.println(s.substring(0,2));
	}
}

词库格式

万能钥匙
丝袜写真
针孔摄像
迷魂香
qq幸运用户抽奖
……