小Alan在最近的開發中遇到了敏感詞過濾,便去網上查閱了很多敏感詞過濾的資料,在這裡也和大家分享一下自己的理解。 在寫之前,小Alan給大家推薦一篇來自http://cmsblogs.com/?p=1031的博文,也會參考部分內容來描述博文。 敏感詞過濾應該是不用給大家過多的解釋吧?講白了就是你在項 ...
小Alan在最近的開發中遇到了敏感詞過濾,便去網上查閱了很多敏感詞過濾的資料,在這裡也和大家分享一下自己的理解。
在寫之前,小Alan給大家推薦一篇來自http://cmsblogs.com/?p=1031的博文,也會參考部分內容來描述博文。
敏感詞過濾應該是不用給大家過多的解釋吧?講白了就是你在項目中輸入某些字(比如輸入xxoo相關的文字時)時要能檢測出來,很多項目中都會有一個敏感詞管理模塊,在敏感詞管理模塊中你可以加入敏感詞,然後根據加入的敏感詞去過濾輸入內容中的敏感詞併進行相應的處理,要麼提示,要麼高亮顯示,要麼直接替換成其它的文字或者符號代替。
敏感詞過濾的做法有很多,我簡單描述我現在理解的幾種:
①查詢資料庫當中的敏感詞,迴圈每一個敏感詞,然後去輸入的文本中從頭到尾搜索一遍,看是否存在此敏感詞,有則做相應的處理,這種方式講白了就是找到一個處理一個。
優點:so easy。用java代碼實現基本沒什麼難度。
缺點:這效率讓我心中奔過十萬匹草泥馬,而且匹配的是不是有些蛋疼,如果是英文時你會發現一個很無語的事情,比如英文a是敏感詞,那我如果是一篇英文文檔,那程式它妹的得處理多少次敏感詞?誰能告訴我?
②傳說中的DFA演算法(有窮自動機),也正是我要給大家分享的,畢竟感覺比較通用,演算法的原理希望大家能夠自己去網上查查資料,這裡就不詳細說明瞭。
優點:至少比上面那sb效率高點。
缺點:對於學過演算法的應該不難,對於沒學過演算法的用起來也不難,就是理解起來有點gg疼,匹配效率也不高,比較耗費記憶體,敏感詞越多,記憶體占用的就越大。
③第三種在這裡要特別說明一下,那就是你自己去寫一個演算法吧,或者在現有的演算法的基礎上去優化,這也是小Alan追求的至高境界之一,如果哪位淫兄有自己的想法一定別忘了小Alan,可以加小Alan的QQ:810104041教小Alan兩招耍耍。
那麼,傳說中的DFA演算法是怎麼實現的呢?
第一步:敏感詞庫初始化(將敏感詞用DFA演算法的原理封裝到敏感詞庫中,敏感詞庫採用HashMap保存),代碼如下:
package com.cfwx.rox.web.sysmgr.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 com.cfwx.rox.web.common.model.entity.SensitiveWord; /** * 敏感詞庫初始化 * * @author AlanLee * */ public class SensitiveWordInit { /** * 敏感詞庫 */ public HashMap sensitiveWordMap; /** * 初始化敏感詞 * * @return */ public Map initKeyWord(List<SensitiveWord> sensitiveWords) { try { // 從敏感詞集合對象中取出敏感詞並封裝到Set集合中 Set<String> keyWordSet = new HashSet<String>(); for (SensitiveWord s : sensitiveWords) { keyWordSet.add(s.getContent().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"); } System.out.println("封裝敏感詞庫過程:"+sensitiveWordMap); } System.out.println("查看敏感詞庫數據:" + sensitiveWordMap); } } }
第二步:寫一個敏感詞過濾工具類,裡面可以寫上自己需要的方法,代碼如下:
package com.cfwx.rox.web.sysmgr.util; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * 敏感詞過濾工具類 * * @author AlanLee * */ public class SensitivewordEngine { /** * 敏感詞庫 */ public static Map sensitiveWordMap = null; /** * 只過濾最小敏感詞 */ public static int minMatchTYpe = 1; /** * 過濾所有敏感詞 */ public static int maxMatchType = 2; /** * 敏感詞庫敏感詞數量 * * @return */ public static 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; } }
第三步:一切都準備就緒,當然是查詢好資料庫當中的敏感詞,並且開始過濾咯,代碼如下:
@SuppressWarnings("rawtypes") @Override public Set<String> sensitiveWordFiltering(String text) { // 初始化敏感詞庫對象 SensitiveWordInit sensitiveWordInit = new SensitiveWordInit(); // 從資料庫中獲取敏感詞對象集合(調用的方法來自Dao層,此方法是service層的實現類) List<SensitiveWord> sensitiveWords = sensitiveWordDao.getSensitiveWordListAll(); // 構建敏感詞庫 Map sensitiveWordMap = sensitiveWordInit.initKeyWord(sensitiveWords); // 傳入SensitivewordEngine類中的敏感詞庫 SensitivewordEngine.sensitiveWordMap = sensitiveWordMap; // 得到敏感詞有哪些,傳入2表示獲取所有敏感詞 Set<String> set = SensitivewordEngine.getSensitiveWord(text, 2); return set; }
最後一步:在Controller層寫一個方法給前端請求,前端獲取到需要的數據併進行相應的處理,代碼如下:
/** * 敏感詞過濾 * * @param text * @return */ @RequestMapping(value = "/word/filter") @ResponseBody public RespVo sensitiveWordFiltering(String text) { RespVo respVo = new RespVo(); try { Set<String> set = sensitiveWordService.sensitiveWordFiltering(text); respVo.setResult(set); } catch (Exception e) { throw new RoxException("過濾敏感詞出錯,請聯繫維護人員"); } return respVo; }
小Alan在代碼中寫了不少的註釋,希望大家能夠動動自己的腦筋好好的理解一下。