關于java中敏感詞檢測的一些總結
之前項目里客戶提出一個需求,需要對系統中使用文本轉化成語音發送的功能進行敏感詞檢測,禁止用戶提交有敏感詞的語音。通過查詢各方面資料,整理了大概幾種方案:
- 項目啟動時對載入敏感詞庫作為緩存(一個大map,敏感詞為key,取任意值為value)。 對請求傳入的文本分詞,遍歷分詞結果,每個分詞在map中查找,如果有值,則請求文本存在敏感詞。
- 把敏感詞庫拼接成一個大的正則表達式,然后直接對文本匹配。
- 使用DFA(確定性有限狀態自動機) DFA算法
對于方案選擇,在網上參考了很多別人的代碼。最簡單的是方法2使用正則表達式,但是據說文本一長會有很大的效率問題。關于方法3DFA算法,由于在學校的時候算法課和編譯原理沒有認真聽講(慚愧= =||),直接就忽略這方法了,所以最后還是決定使用方法1。
其實方法1還是有很多可以改進的方法,后來又參考了這個帖子12樓中的方法,使用索引數組加關聯數組的方式,提高了檢索效率,甚至連分詞的步驟都省掉了。整個實現代碼如下。
import org.apache.commons.lang.StringUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* User: eternity
* Date: 2014/8/11
* Time: 16:17
* 敏感詞檢測類
* 敏感詞檢測初始化規則:
* 將敏感詞從詞庫載入,按照2字、3字、4字、5字等字數各生成一個敏感詞哈希表。
* 在將這些哈希表組成一個數組banWordsList,數組下標表示該敏感詞表字數
* banWordsList[2] = {某馬:true,屏蔽:true,啦啦:true};
* banWordsList[3] = {某個馬:true,三個字:true,啦啦啦:true,小廣告:true};
* banWordsList[4] = {某個壞銀:true,四個字符:true,哈哈哈哈:true,就愛鳳姐:true};
* banWordsList[5] = {某個大法好:true,五個敏感字:true};
* 根據上面幾組組敏感詞,自動生成以下索引
* 生成規則為,索引名是敏感詞第一個字,值是一個int
* 該int的規則為,該int轉換成二進制時,第i位為1表示上面4表存在長度為i的敏感詞,否則不存在長度為i的敏感詞(10000)
* wordIndex = {二:0x04,三:0x08,四:0x10,五:0x20,某:0x3c,啦:0x0c,哈:0x10,小:0x08,就:0x10};
*
* 檢查規則如下:
* 1,逐字檢驗,是否該字在wordIndex索引表中。
* 2,如果不在表中,繼續檢驗
* 3,如果在表中,根據索引表該鍵的值,取此字以及此字后的若干字檢驗詳細表banWordsList[索引詞長]。
*
* 檢驗例子
* 有一段如下文字,檢驗其是否包含敏感詞:
“我就打小廣告,氣死版主”
——檢測“我”
|-不在索引表
——檢測“就”
|-在索引表
|-“就”的索引值是0x10,表示有4字以“就”開頭的敏感詞
|-取“就”和后面的字共4個,組成“就打小廣”
|-查4字敏感詞表,沒有這項,繼續
——檢測“打”
|-不在索引表
——檢測“小”
|-在索引表
|-索引值是0x08,表示有3字長度的敏感詞
|-取“小”和“小”后面的字,共3個字組成一個詞“小廣告”
|-“小廣告”在3字敏感詞中,此帖包含敏感詞,禁止發布
*/
public class BanWordsUtil {
// public Logger logger = Logger.getLogger(this.getClass());
public static final int WORDS_MAX_LENGTH = 10;
public static final String BAN_WORDS_LIB_FILE_NAME = "banWords.txt";
//敏感詞列表
public static Map[] banWordsList = null;
//敏感詞索引
public static Map<String, Integer> wordIndex = new HashMap<String, Integer>();
/*
* 初始化敏感詞庫
*/
public static void initBanWordsList() throws IOException {
if (banWordsList == null) {
banWordsList = new Map[WORDS_MAX_LENGTH];
for (int i = 0; i < banWordsList.length; i++) {
banWordsList[i] = new HashMap<String, String>();
}
}
//敏感詞詞庫所在目錄,這里為txt文本,一個敏感詞一行
String path = BanWordsUtil.class.getClassLoader()
.getResource(BAN_WORDS_LIB_FILE_NAME)
.getPath();
System.out.println(path);
List<String> words = FileUtils.readLines(FileUtils.getFile(path));
for (String w : words) {
if (StringUtils.isNotBlank(w)) {
//將敏感詞按長度存入map
banWordsList[w.length()].put(w.toLowerCase(), "");
Integer index = wordIndex.get(w.substring(0, 1));
//生成敏感詞索引,存入map
if (index == null) {
index = 0;
}
int x = (int) Math.pow(2, w.length());
index = (index | x);
wordIndex.put(w.substring(0, 1), index);
}
}
}
/**
* 檢索敏感詞
* @param content
* @return
*/
public static List<String> searchBanWords(String content) {
if (banWordsList == null) {
try {
initBanWordsList();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
List<String> result = new ArrayList<String>();
for (int i = 0; i < content.length(); i++) {
Integer index = wordIndex.get(content.substring(i, i + 1));
int p = 0;
while ((index != null) && (index > 0)) {
p++;
index = index >> 1;
String sub = "";
if ((i + p) < (content.length() - 1)) {
sub = content.substring(i, i + p);
} else {
sub = content.substring(i);
}
if (((index % 2) == 1) && banWordsList[p].containsKey(sub)) {
result.add(content.substring(i, i + p));
// System.out.println("找到敏感詞:"+content.substring(i,i+p));
}
}
}
return result;
}
public static void main(String[] args) throws IOException {
String content = "含有敏感詞的測試語句。";
BanWordsUtil.initBanWordsList();
List<String> banWordList = BanWordsUtil.searchBanWords(content);
for(String s : banWordLis){
System.out.println("找到敏感詞:"+s);
}
}
}上面測試語文本里面其實沒有敏感詞(我也怕被屏蔽XD),測試的時候隨便加入幾個敏感詞都能檢測出來的。這樣就實現了一個簡易又快速的敏感詞檢測,當然如果有需要比較復雜的檢測邏輯(比如說“彈吉他媽媽真漂亮”這樣的),還是要用到分詞工具把詞拆分一下的。來自:http://my.oschina.net/u/1010578/blog/308904
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!