天天看點

Java敏感詞過濾

方案一:使用String類的contains()

contains()方法用于判斷字元串中是否包含指定的字元或字元串。

public static void main(String[] args) {
        String a = "敏感詞";
        String b = "我的敏感詞";
        System.out.println(b.contains(a));
        if (b.contains(a)) {
            System.out.println("有敏感詞");
        }
    }           

方案二:使用String類的indexOf()

indexOf()方法不僅能判斷字元串中是否能包含某個字元,還可以傳回對應的下标,而且能找出所有相同字元對應的下标。

public static void main(String[] args) {
        String a = "敏感詞";
        String b = "我的敏感詞";
        System.out.println(b.indexOf(a));
        if (b.indexOf(a) > 0) {
            System.out.println("有敏感詞");
        }
    }           

注意:這兩個方案有一個很大的問題是,随着敏感詞數量的增多,敏感詞檢測的時間會呈線性增長。項目的敏感詞數量隻有幾十個,使用這種方案不會存在太大的性能問題。但是如果項目中有成千上萬個敏感詞,使用這種方案就會很耗CPU了。

方案三:DAF有窮自動機算法算法

DFA的算法,即Deterministic Finite Automaton算法,翻譯成中文就是确定有窮自動機算法。它的基本思想是基于狀态轉移來檢索敏感詞,隻需要掃描一次待檢測文本,就能對所有敏感詞進行檢測,是以效率比方案一高不少。

具體實作代碼:

controller類

@ApiOperation(value = "是否為敏感詞", notes = "是否為敏感詞")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "token", value = "token令牌", required = false, paramType = "header"),
    })
    @ControllerLogger
    @UnAuthentication
    @PostMapping(value = "/isSensitive")
    public JsonResult isSensitive(@ApiParam(name = "words", value = "字元串", required = true) @RequestParam String words,
                                  @ApiParam(name = "type", value = "1商品拍品2昵稱3店鋪名", required = true) @RequestParam Integer type) {
        JsonResult jsonResult = new JsonResult();
        HashSet<String> set = new HashSet<>();
        //這裡我的敏感詞存在資料庫 大家結合自己的業務需求改動 可以存在txt檔案中或者excel
        QueryFilter filter = new QueryFilter(DsSensitiveWords.class);
        filter.addFilter("type=", type);
        List<DsSensitiveWords> list = dsSensitiveWordsService.find(filter);
        for (DsSensitiveWords dsSensitiveWords : list) {
            //從資料庫中取出敏感詞 且用set去重
            set.add(dsSensitiveWords.getWords());
        }
        return jsonResult.setSuccess(true).setObj(SensitiveFilterUtil.checkTxt(words, set));
    }           
SensitiveFilterUtil類      
/**
 * 敏感詞過濾工具類
 *
 * @author yunshang
 */

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

@SuppressWarnings({"unchecked", "rawtypes"})
public class SensitiveFilterUtil {
    /**
     * 敏感詞集合
     */
    public static HashMap sensitiveWordMap;

    /**
     * 初始化敏感詞庫,建構DFA算法模型
     */
    public static void initContext(HashSet<String> set) {
        initSensitiveWordMap(set);
    }

    /**
     * 初始化敏感詞庫,建構DFA算法模型
     *
     * @param sensitiveWordSet 敏感詞庫
     */
    private static void initSensitiveWordMap(Set<String> sensitiveWordSet) {
        //初始化敏感詞容器,減少擴容操作
        sensitiveWordMap = new HashMap<String, String>(sensitiveWordSet.size());
        Map<Object, Object> temp;
        Map<Object, Object> newWorMap;
        //周遊sensitiveWordSet
        for (String key : sensitiveWordSet) {
            temp = sensitiveWordMap;
            for (int i = 0; i < key.length(); i++) {
                //轉換成char型
                char keyChar = key.charAt(i);
                //庫中擷取關鍵字
                Object wordMap = temp.get(keyChar);
                //如果存在該key,直接指派,用于下一個循環擷取
                if (wordMap != null) {
                    temp = (Map) wordMap;
                } else {
                    //不存在則,則建構一個map,同時将isEnd設定為0,因為他不是最後一個
                    newWorMap = new HashMap<>();
                    //不是最後一個
                    newWorMap.put("isEnd", "0");
                    temp.put(keyChar, newWorMap);
                    temp = newWorMap;
                }
                //最後一個
                if (i == key.length() - 1) temp.put("isEnd", "1");
            }
        }
    }

    /**
     * 判斷文字是否包含敏感字元
     * <p>
     * 文本
     * <p>
     * 若包含傳回true,否則傳回false
     */
    public static boolean contains(String txt) {
        boolean flag = false;
        for (int i = 0; i < txt.length(); i++) {
            int matchFlag = checkSensitiveWord(txt, i); //判斷是否包含敏感字元
            if (matchFlag > 0) {//大于0存在,傳回true
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 檢查文字中是否包含敏感字元,檢查規則如下:
     *
     * @param txt
     * @param beginIndex
     * @return 如果存在, 則傳回敏感詞字元的長度, 不存在傳回0
     */
    private static int checkSensitiveWord(String txt, int beginIndex) {
        //敏感詞結束辨別位:用于敏感詞隻有1位的情況
        boolean flag = false;
        //比對辨別數預設為0
        int matchFlag = 0;
        char word;
        Map nowMap = sensitiveWordMap;
        for (int i = beginIndex; i < txt.length(); i++) {
            word = txt.charAt(i);
            //擷取指定key
            nowMap = (Map) nowMap.get(word);
            if (nowMap != null) {//存在,則判斷是否為最後一個
                //找到相應key,比對辨別+1
                matchFlag++;
                //如果為最後一個比對規則,結束循環,傳回比對辨別數
                if ("1".equals(nowMap.get("isEnd"))) {
                    //結束标志位為true
                    flag = true;
                }
            } else {//不存在,直接傳回
                break;
            }
        }
        if (matchFlag < 2 || !flag) {//長度必須大于等于1,為詞
            matchFlag = 0;
        }
        return matchFlag;
    }

    /**
     * 擷取文字中的敏感詞
     * <p>
     * txt文字
     */
    public static HashSet getSensitiveWord(String txt) {
        HashSet hashSet = new HashSet();
        for (int i = 0; i < txt.length(); i++) {
            //判斷是否包含敏感字元
            int length = checkSensitiveWord(txt, i);
            if (length > 0) {//存在,加入list中
                hashSet.add(txt.substring(i, i + length));
                i = i + length - 1;//減1的原因,是因為for會自增
            }
        }
        return hashSet;
    }


    /**
     * context是要校驗的内容。傳回結果是list,為空說明沒有敏感詞
     *
     * @param context
     * @return
     */
    public static HashSet checkTxt(String context, HashSet<String> set) {
        initContext(set);
        //包含敏感詞傳回所有敏感詞資料
        return getSensitiveWord(context);
    }
}           

結果