Java利用DFA演算法實現敏感詞過濾
阿新 • • 發佈:2019-02-14
前言:
敏感詞文字過濾是一個網站必不可少的功能,如何設計一個好的、高效的過濾演算法是非常有必要的。作為一般開發人員來說首先考慮的肯定是簡單的匹配,這樣是可以實現功能,但效率比較慢,在高階一點的就是正則表示式,比前一個好一點,但終究還是一丘之貉,非常遺憾,兩種方法都不可取。當然,在我意識裡沒有我也沒有認知到那個演算法可以解決問題,但是百度知道,以下就是學習的DFA演算法簡單介紹和功能實現。
一、DFA演算法簡介
DFA全稱為:Deterministic Finite Automaton,即確定有窮自動機。其特徵為:有一個有限狀態集合和一些從一個狀態通向另一個狀態的邊,每條邊上標記有一個符號,其中一個狀態是初態,某些狀態是終態。但不同於不確定的有限自動機,DFA中不會有從同一狀態出發的兩條邊標誌有相同的符號。
簡單點說就是,它是是通過event和當前的state得到下一個state,即event+state=nextstate。理解為系統中有多個節點,通過傳遞進入的event,來確定走哪個路由至另一個節點,而節點是有限的。
二、程式碼示例
1、敏感詞庫初始化(將敏感詞用DFA演算法的原理封裝到敏感詞庫中,敏感詞庫採用HashMap儲存)
package com.fintech.modules.base.util; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fintech.modules.sms.black.dto.SmsBlackDict; /** * @Description: 敏感詞庫初始化 * @author lc * @date 2018年10月9日 */ public class SensitiveWordInit { private static Logger logger = LoggerFactory.getLogger(SensitiveWordInit.class); /** * 敏感詞庫 */ public HashMap sensitiveWordMap; /** * 初始化敏感詞 * @return */ public Map initKeyWord(List<SmsBlackDict> sensitiveWords) { try { // 從敏感詞集合物件中取出敏感詞並封裝到Set集合中 Set<String> keyWordSet = new HashSet<String>(); for (SmsBlackDict s : sensitiveWords) { keyWordSet.add(s.getName().trim()); } // 將敏感詞庫加入到HashMap中 addSensitiveWordToHashMap(keyWordSet); } catch (Exception e) { e.printStackTrace(); } return sensitiveWordMap; } /** * 封裝敏感詞庫 * @param keyWordSet */ @SuppressWarnings("rawtypes") private void addSensitiveWordToHashMap(Set<String> keyWordSet) { // 初始化HashMap物件並控制容器的大小 sensitiveWordMap = new HashMap(keyWordSet.size()); // 敏感詞 String key = null; // 用來按照相應的格式儲存敏感詞庫資料 Map nowMap = null; // 用來輔助構建敏感詞庫 Map<String, String> newWorMap = null; // 使用一個迭代器來迴圈敏感詞集合 Iterator<String> iterator = keyWordSet.iterator(); while (iterator.hasNext()) { key = iterator.next(); // 等於敏感詞庫,HashMap物件在記憶體中佔用的是同一個地址,所以此nowMap物件的變化,sensitiveWordMap物件也會跟著改變 nowMap = sensitiveWordMap; for (int i = 0; i < key.length(); i++) { // 擷取敏感詞當中的字,在敏感詞庫中字為HashMap物件的Key鍵值 char keyChar = key.charAt(i); // 判斷這個字是否存在於敏感詞庫中 Object wordMap = nowMap.get(keyChar); if (wordMap != null) { nowMap = (Map) wordMap; } else { newWorMap = new HashMap<String, String>(); newWorMap.put("isEnd", "0"); nowMap.put(keyChar, newWorMap); nowMap = newWorMap; } // 如果該字是當前敏感詞的最後一個字,則標識為結尾字 if (i == key.length() - 1) { nowMap.put("isEnd", "1"); } logger.warn("封裝敏感詞庫過程:"+sensitiveWordMap); } logger.warn("檢視敏感詞庫資料:" + sensitiveWordMap); } } }
2、敏感詞過濾工具類
package com.fintech.modules.base.util; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * @Description: 敏感詞過濾工具類 * @author lc * @date 2018年10月10日 */ public class SensitiveWordEngine { /** * 敏感詞庫 */ public static Map sensitiveWordMap = null; /** * 只過濾最小敏感詞 */ public static int minMatchTYpe = 1; /** * 過濾所有敏感詞 */ public static int maxMatchType = 2; /** * 敏感詞庫敏感詞數量 * * @return */ public int getWordSize() { if (SensitiveWordEngine.sensitiveWordMap == null) { return 0; } return SensitiveWordEngine.sensitiveWordMap.size(); } /** * 是否包含敏感詞 * * @param txt * @param matchType * @return */ public static boolean isContaintSensitiveWord(String txt, int matchType) { boolean flag = false; for (int i = 0; i < txt.length(); i++) { int matchFlag = checkSensitiveWord(txt, i, matchType); if (matchFlag > 0) { flag = true; } } return flag; } /** * 獲取敏感詞內容 * * @param txt * @param matchType * @return 敏感詞內容 */ public static Set<String> getSensitiveWord(String txt, int matchType) { Set<String> sensitiveWordList = new HashSet<String>(); for (int i = 0; i < txt.length(); i++) { int length = checkSensitiveWord(txt, i, matchType); if (length > 0) { // 將檢測出的敏感詞儲存到集合中 sensitiveWordList.add(txt.substring(i, i + length)); i = i + length - 1; } } return sensitiveWordList; } /** * 替換敏感詞 * * @param txt * @param matchType * @param replaceChar * @return */ public static String replaceSensitiveWord(String txt, int matchType, String replaceChar) { String resultTxt = txt; Set<String> set = getSensitiveWord(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 replaceChar * @param length * @return */ private static String getReplaceChars(String replaceChar, int length) { String resultReplace = replaceChar; for (int i = 1; i < length; i++) { resultReplace += replaceChar; } return resultReplace; } /** * 檢查敏感詞數量 * * @param txt * @param beginIndex * @param matchType * @return */ public static int checkSensitiveWord(String txt, int beginIndex, int matchType) { boolean flag = false; // 記錄敏感詞數量 int matchFlag = 0; char word = 0; Map nowMap = SensitiveWordEngine.sensitiveWordMap; for (int i = beginIndex; i < txt.length(); i++) { word = txt.charAt(i); // 判斷該字是否存在於敏感詞庫中 nowMap = (Map) nowMap.get(word); if (nowMap != null) { matchFlag++; // 判斷是否是敏感詞的結尾字,如果是結尾字則判斷是否繼續檢測 if ("1".equals(nowMap.get("isEnd"))) { flag = true; // 判斷過濾型別,如果是小過濾則跳出迴圈,否則繼續迴圈 if (SensitiveWordEngine.minMatchTYpe == matchType) { break; } } } else { break; } } if (!flag) { matchFlag = 0; } return matchFlag; } }
3、呼叫
/**
* @Description: 是否包含敏感詞
* @author lc
*/
public Boolean isContaintSensitiveWord(String message){
logger.info("[通用簡訊請求]是否包含敏感詞驗證, 簡訊內容為: {}", message);
Map<String, Object> paramMap = new HashMap<String, Object>();
Boolean boo = false;
try {
paramMap.put("valid", PublicEnum.YES.getCode());
paramMap.put("type", PublicEnum.N.getCode());
// 從資料庫中獲取敏感詞物件集合
List<SmsBlackDict> list = blackDictMapper.listBlack(paramMap);
// 初始化敏感詞庫物件
SensitiveWordInit sensitiveWordInit = new SensitiveWordInit();
// 構建敏感詞庫
Map sensitiveWordMap = sensitiveWordInit.initKeyWord(list);
// 傳入SensitivewordEngine類中的敏感詞庫
SensitiveWordEngine.sensitiveWordMap = sensitiveWordMap;
boo = SensitiveWordEngine.isContaintSensitiveWord(message, 2);
} catch (Exception e) {
logger.error("[通用簡訊請求]是否包含敏感詞驗證異常!{}", e.getMessage());
}
return boo;
}
/**
* @Description: 獲取敏感詞內容
* @author lc
*/
public Set<String> getSensitiveWord(BaseMessage baseMessage){
logger.info("[通用簡訊請求]獲取敏感詞內容, 簡訊內容為: {}", baseMessage.getMessage());
Map<String, Object> paramMap = new HashMap<String, Object>();
Set<String> sensitiveWordList = null;
try {
paramMap.put("valid", PublicEnum.YES.getCode());
paramMap.put("type", PublicEnum.N.getCode());
// 從資料庫中獲取敏感詞物件集合
List<SmsBlackDict> list = blackDictMapper.listBlack(paramMap);
// 初始化敏感詞庫物件
SensitiveWordInit sensitiveWordInit = new SensitiveWordInit();
// 構建敏感詞庫
Map sensitiveWordMap = sensitiveWordInit.initKeyWord(list);
// 傳入SensitivewordEngine類中的敏感詞庫
SensitiveWordEngine.sensitiveWordMap = sensitiveWordMap;
sensitiveWordList = SensitiveWordEngine.getSensitiveWord(baseMessage.getMessage(), 2);
logger.info("[通用簡訊請求]獲取敏感詞內容, 敏感詞為: {}", sensitiveWordList);
// 留存攔截記錄
SmsBlackInterceptRecord record = new SmsBlackInterceptRecord(baseMessage, sensitiveWordList.toString());
interceptRecordService.insert(record);
} catch (Exception e) {
logger.error("[通用簡訊請求]獲取敏感詞內容異常!{}", e.getMessage());
}
return sensitiveWordList;
}
註釋:程式碼中包含自己的業務邏輯程式碼,只要改成自己的就可以使用