Java实现DFA算法敏感词过滤
Java實現(xiàn)DFA算法敏感詞過濾。
文章目錄
- 一. 應用場景
- 二. 實現(xiàn)思路
- 三. 源碼實現(xiàn)
一. 應用場景
模擬非法詞匯自動替換成*字符,且敏感詞匯支持動態(tài)調(diào)整。
效果如下,若配置了敏感詞:今天,則當用戶在輸入:今天,天氣真不錯。實際應該輸出:**,天氣真不錯
二. 實現(xiàn)思路
- 使用MySQL作為敏感詞庫
- 使用DFA算法實現(xiàn)文字過濾
Q:什么是DFA?
DFA全稱為:Deterministic Finite Automaton,即確定有窮自動機。其特征為:有一個有限狀態(tài)集合和一些從一個狀態(tài)通向另一個狀態(tài)的邊,每條邊上標記有一個符號,其中一個狀態(tài)是初態(tài),某些狀態(tài)是終態(tài)。但不同于不確定的有限自動機,DFA中不會有從同一狀態(tài)出發(fā)的兩條邊標志有相同的符號。
確定:狀態(tài)以及引起狀態(tài)轉(zhuǎn)換的事件都是可確定的,不存在“意外”。
有窮:狀態(tài)以及事件的數(shù)量都是可窮舉的。
DFA算法模型如下:
state_event_dict = {"匹": {"配": {"算": {"法": {"is_end": True},"is_end": False},"關(guān)": {"鍵": {"詞": {"is_end": True},"is_end": False},"is_end": False},"is_end": False},"is_end": False},"信": {"息": {"抽": {"取": {"is_end": True},"is_end": False},"is_end": False},"is_end": False} }用通俗易懂的話來解釋,就是將數(shù)據(jù)庫中的敏感詞進行建立樹結(jié)構(gòu),舉個例子,數(shù)據(jù)庫的敏感詞匯有三個,分別是:今天,今天很好,今天真煩。
建立樹結(jié)構(gòu),并且標記好三個詞匯的非葉子節(jié)點和葉子節(jié)點(即最后一個字符是葉子節(jié)點),并且制定好匹配規(guī)則,只有碰到葉子節(jié)點才算一次過濾:
模擬用戶輸入以下一句話:
我覺得今天還行。接下來我們將這句話逐個字拆分并將每一個字代入到上面的樹狀結(jié)構(gòu)圖中。
前面三個字不在敏感詞樹中直接可以跳過,直到遇到了今這個字,發(fā)現(xiàn)匹配上了敏感詞樹,接下來看樹狀結(jié)構(gòu)發(fā)現(xiàn)只有下一個字是天才能被捕獲,繼續(xù)往下走果然發(fā)現(xiàn)天這個字也匹配到了;
再接著走發(fā)現(xiàn)在樹結(jié)構(gòu)中天這個字的下一個字只有匹配到很或者真才能繼續(xù)匹配,而用戶輸入的下一個字是還,第一步判斷當前已經(jīng)走到了葉子節(jié)點,故先將今天置為敏感詞,然后將還這個字從Top頂節(jié)點中重新繼續(xù)流轉(zhuǎn),發(fā)現(xiàn)無法匹配。
過濾結(jié)束,且當前的節(jié)點是葉子節(jié)點,故這句話僅僅被敏感詞過濾了今天這兩個字,最終的過濾結(jié)果應該是:
我覺得**還行要注意的是只有完整的碰到過一次葉子節(jié)點才算一次過濾,且一句話可以被多次過濾。以上就是針對DFA算法的簡單說明。
三. 源碼實現(xiàn)
理論知識加身,接下來就該實干了。Talk is cheap, show me the code:
import java.util.HashMap; import java.util.Map;/*** @author: Vainycos* @description 敏感字樹生成器* @date: 2021/12/4 12:45*/ public class TreeGenerator {/*** 將指定的詞分成字構(gòu)建到一棵樹中。** @param tree* 字樹* @param sensitiveWord* 敏感詞詞* @return*/public static void addWord2Tree(Map<String, Map> tree, String sensitiveWord) {addWord2Tree(tree, sensitiveWord, 0);}private static Map<String, Map> addWord2Tree(Map<String, Map> tree,String word, int index) {if (index == word.length()) {tree.put(Finder.TREE_END_KEY, generateWordMap(word));return tree;}String next = word.substring(index, index + 1);Map<String, Map> subTree = tree.get(next);if (subTree == null) {subTree = new HashMap<String, Map>();}tree.put(next, addWord2Tree(subTree, word, index + 1));return tree;}private static Map<String, Object> generateWordMap(String word) {Map<String, Object> wordMap = new HashMap<String, Object>();wordMap.put(Finder.WORD_VALUE, word);wordMap.put(Finder.WORD_LENGTH, word.length());return wordMap;} } import lombok.extern.slf4j.Slf4j;import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap;/*** * 敏感詞過濾* */ @Slf4j @SuppressWarnings({ "rawtypes", "unchecked" }) public class Finder {// 敏感詞private static Set<String> WORDS = new HashSet<String>();// 由敏感詞生成的字樹private static Map<String, Map> TREE = new ConcurrentHashMap<String, Map>();// 默認敏感詞分割符public static final String DEFAULT_SEPARATOR = ",";/* 在樹當中標志一個詞的結(jié)束 */public static final String TREE_END_KEY = "^";// 敏感詞value標記public static final String WORD_VALUE = "v";// 敏感詞長度標記public static final String WORD_LENGTH = "l";// 默認替換符public static final char DEFAULT_REPLACEMENT = '*';// 默認起始標記public static final String DEFAULT_START_TAG = "<font color=\"red\">";// 默認結(jié)束標記public static final String DEFAULT_END_TAG = "</font>";public Finder(String[] words) {addSensitiveWords(words);}/*** 刪除所有敏感詞*/public static void clearSensitiveWords() {WORDS.clear();TREE.clear();}/*** * 添加敏感詞* * @author hymer* @param words*/public static void addSensitiveWords(String[] words) {if (words == null || words.length == 0) {return;}check(words);addWords(words);}/*** * 添加敏感詞* * @author hymer* @param words* @param separator*/public static void addSensitiveWords(String words, String separator) {if (words != null && !"".equals(words.trim())) {check(words);String[] sensitiveWords = words.split(separator);addWords(sensitiveWords);}}/*** * 添加敏感詞,默認使用,分割* * @author hymer* @param words*/public static void addSensitiveWords(String words) {check(words);addSensitiveWords(words, DEFAULT_SEPARATOR);}/*** 刪除敏感詞* * @param words*/public static void removeSensitiveWords(String words) {check(words);removeSensitiveWords(words, DEFAULT_SEPARATOR);}/*** 刪除敏感詞* * @param words*/public static void removeSensitiveWords(String words, String separator) {if (words != null && !"".equals(words.trim())) {check(words);String[] sensitiveWords = words.split(separator);removeWords(sensitiveWords);}}/*** 刪除敏感詞* * @param words*/public static void removeSensitiveWords(String[] words) {if (words == null || words.length == 0) {return;}check(words);removeWords(words);}/*** * 找出文本中的敏感詞* * @author hymer* @param text* @return*/public static Set<String> find(String text) {return new TextAnalysis().analysis(TREE, text);}/*** 替換文本中的敏感詞* * @param text* 含敏感詞的文本* @return*/public static String replace(String text) {return new TextAnalysis().replace(TREE, text, DEFAULT_REPLACEMENT);}/*** 替換文本中的敏感詞* * @param text* 含敏感詞的文本* @param replacement* 替換字符* @return*/public static String replace(String text, Character replacement) {return new TextAnalysis().replace(TREE, text, replacement);}/*** * 過濾文本,并標記出敏感詞,默認使用HTML中紅色font標記* * @author hymer* @param text* @return*/public static String filter(String text) {return new TextAnalysis().mark(TREE, text, DEFAULT_START_TAG, DEFAULT_END_TAG);}/*** * 過濾文本,并標記出敏感詞* * @author hymer* @param text* @param startTag* @param endTag* @return*/public static String filter(String text, String startTag, String endTag) {return new TextAnalysis().mark(TREE, text, startTag, endTag);}private static void check(String... words) {for (String word : words) {if (word != null && word.contains(TREE_END_KEY)) {throw new RuntimeException("包含非法字符:" + TREE_END_KEY);}}}private static void addWords(String... sensitiveWords) {for (String word : sensitiveWords) {if (word != null && !word.trim().equals("")) {word = word.trim();int len = word.length();if (len > 1024) {throw new RuntimeException("敏感詞太長[最長1024]!");}// 添加該詞,如果未重復,則添加到treeif (WORDS.add(word)) {TreeGenerator.addWord2Tree(TREE, word);}}}log.info("當前敏感詞數(shù)量:", WORDS.size());}private static void removeWords(String... sensitiveWords) {for (String word : sensitiveWords) {if (word != null && !word.trim().equals("")) {word = word.trim();WORDS.remove(word);}}TREE.clear();addWords(WORDS.toArray(new String[WORDS.size()]));}@SuppressWarnings("unused")private static void printTree(Map<String, Map> wordTree, int level) {if (wordTree == null || wordTree.isEmpty()) {return;}Iterator<String> it = wordTree.keySet().iterator();while (it.hasNext()) {String next = it.next();Object tmp = wordTree.get(next);if (tmp instanceof Map) {printTree((Map) tmp, level + 1);}}}}以上就是關(guān)鍵代碼,接下來我們測試一下效果:
public static void main(String[] args) {String content = "我覺得今天還行。";// 定義敏感詞庫 后續(xù)可以對接數(shù)據(jù)庫String[] sensitiveWords = {"今天", "今天很好", "今天真煩"};// 設置敏感詞庫Finder.addSensitiveWords(sensitiveWords);// 敏感詞過濾替換字符為*String replace = Finder.replace(content, '*');// 輸出結(jié)果:我覺得**還行。System.out.println(replace); }發(fā)現(xiàn)效果還不錯。至此,一個簡單的DFA算法實現(xiàn)敏感詞過濾功能就實現(xiàn)了。
參考資料:
- Java實現(xiàn)敏感詞過濾
- java 敏感詞工具實現(xiàn)思路
- 參考實現(xiàn)代碼
總結(jié)
以上是生活随笔為你收集整理的Java实现DFA算法敏感词过滤的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GARFIELD@01-19-2005
- 下一篇: 意外地调用了方法或属性访问