1. 程式人生 > >java敏感詞過濾(詞庫+演算法)高效率驗證

java敏感詞過濾(詞庫+演算法)高效率驗證

需求:使用者輸入一段文字,驗證是否包含敏感詞,以及具體的是哪些敏感詞,替換為*等....

1.需要一個詞庫,我這裡就是一個從github下載的一個txt檔案。已轉存到百度網盤,點選下載詞庫,提取碼:tk3g

2.DFA演算法,效能卓越,請放心使用,直接上java程式碼:

package com.vk.updoc.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**參考DFA演算法demo:http://blog.csdn.net/chenssy/article/details/26961957*/
public class BadWordUtil2 {
	public static String filePath = "D:\\dictionary.txt";//敏感詞庫檔案路徑
	public static Set<String> words;
	public static Map<String,String> wordMap;
	public static int minMatchTYpe = 1;      //最小匹配規則
	public static int maxMatchType = 2;      //最大匹配規則
	static{
		BadWordUtil2.words = readTxtByLine(filePath);
		addBadWordToHashMap(BadWordUtil2.words);
	}
	 public static Set<String> readTxtByLine(String path){  
    	Set<String> keyWordSet = new HashSet<String>();
        File file=new File(path);  
        if(!file.exists()){      //檔案流是否存在
        	return keyWordSet;
        }
        BufferedReader reader=null;  
        String temp=null;  
        //int line=1;  
        try{  
            //reader=new BufferedReader(new FileReader(file));這樣在web執行的時候,讀取會亂碼 
            reader=new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));  
            while((temp=reader.readLine())!=null){  
                //System.out.println("line"+line+":"+temp);  
                keyWordSet.add(temp);
                //line++;  
            }  
        } catch(Exception e){  
            e.printStackTrace();  
        } finally{  
            if(reader!=null){  
                try{  
                    reader.close();  
                }catch(Exception e){  
                    e.printStackTrace();  
                }  
            }  
        }
        return keyWordSet;
    }
	/**
     * 檢查文字中是否包含敏感字元,檢查規則如下:<br>
     * @param txt
     * @param beginIndex
     * @param matchType
     * @return,如果存在,則返回敏感詞字元的長度,不存在返回0
     * @version 1.0
     */
    @SuppressWarnings({ "rawtypes"})
    public static 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
	 */
	public static 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
	 */
	public static 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
	 */
	public static 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 static 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 static 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");    //最後一個
                }
            }
        }
    }
	
	
	public static void main(String[] args) {
		Set<String> s = BadWordUtil2.words;
		Map<String,String> map = BadWordUtil2.wordMap;
		
		System.out.println("敏感詞的數量:" + BadWordUtil2.wordMap.size());
		String string = "太多的傷感情懷也許只侷限於飼養基地 熒幕中的情節,主人公嘗試著去用某種方式漸漸的很瀟灑地釋自殺指南懷那些自己經歷的傷感。"
						+ "然後法輪功 我們的扮演的角色就是跟隨著主人公的喜紅客聯盟 怒哀樂而過於牽強的把自己的情感也附加於銀幕情節中,然後感動就流淚,"
						+ "難過就躺在某一個人的懷裡盡情的闡述心扉或者手機卡複製器一個人一杯紅酒一部電影在夜三級片 深人靜的晚上,關上電話靜靜的發呆著。";
		System.out.println("待檢測語句字數:" + string.length());
		long beginTime = System.currentTimeMillis();
		Set<String> set = BadWordUtil2.getBadWord(string, 2);
		Boolean i = BadWordUtil2.isContaintBadWord(string, 2);
		Boolean i2 = BadWordUtil2.isContaintBadWord("粉飾太平", 2);
		Boolean i22 = BadWordUtil2.isContaintBadWord("粉飾太平", 1);
		Boolean i3 = BadWordUtil2.isContaintBadWord("粉飾", 2);
		Boolean i33 = BadWordUtil2.isContaintBadWord("粉飾", 1);
		Boolean i4 = BadWordUtil2.isContaintBadWord("太平", 2);
		Boolean i44 = BadWordUtil2.isContaintBadWord("太平", 1);
		Boolean i5 = BadWordUtil2.isContaintBadWord("個人崇拜", 2);
		Boolean i55 = BadWordUtil2.isContaintBadWord("個人崇拜", 1);
		Boolean i6 = BadWordUtil2.isContaintBadWord("個人", 2);
		Boolean i66 = BadWordUtil2.isContaintBadWord("個人", 1);
		Boolean i7 = BadWordUtil2.isContaintBadWord("崇拜", 2);
		Boolean i77 = BadWordUtil2.isContaintBadWord("崇拜", 1);
		long endTime = System.currentTimeMillis();
		System.out.println("語句中包含敏感詞的個數為:" + set.size() + "。包含:" + set);
		System.out.println("總共消耗時間為:" + (endTime - beginTime));
	}
}

直接執行main方法即可測試,無須多餘jar包

參考:http://blog.csdn.net/chenssy/article/details/26961957