Java實現(xiàn)DFA算法對敏感詞、廣告詞過濾功能示例
一、前言
開發(fā)中經(jīng)常要處理用戶一些文字的提交,所以涉及到了敏感詞過濾的功能,參考資料中DFA有窮狀態(tài)機算法的實現(xiàn),創(chuàng)建有向圖。完成了對敏感詞、廣告詞的過濾,而且效率較好,所以分享一下。
具體實現(xiàn):
1、匹配大小寫過濾
2、匹配全角半角過濾
3、匹配過濾停頓詞過濾。
4、敏感詞重復詞過濾。
例如:
支持如下類型類型過濾檢測:
fuck 全小寫
FuCk 大小寫
fuck全角半角
f!!!u&c ###k 停頓詞
fffuuuucccckkk 重復詞
敏感詞過濾的做法有很多,我簡單描述我現(xiàn)在理解的幾種:
①查詢數(shù)據(jù)庫當中的敏感詞,循環(huán)每一個敏感詞,然后去輸入的文本中從頭到尾搜索一遍,看是否存在此敏感詞,有則做相
應的處理,這種方式講白了就是找到一個處理一個。
優(yōu)點:so easy。用java代碼實現(xiàn)基本沒什么難度。
缺點:這效率讓我心中奔過十萬匹草泥馬,而且匹配的是不是有些蛋疼,如果是英文時你會發(fā)現(xiàn)一個很無語的事情,比如英文
a是敏感詞,那我如果是一篇英文文檔,那程序它妹的得處理多少次敏感詞?誰能告訴我?
②傳說中的DFA算法(有窮自動機),也正是我要給大家分享的,畢竟感覺比較通用,算法的原理希望大家能夠自己去網(wǎng)上查查
資料,這里就不詳細說明了。
優(yōu)點:至少比上面那sb效率高點。
缺點:對于學過算法的應該不難,對于沒學過算法的用起來也不難,就是理解起來有點gg疼,匹配效率也不高,比較耗費內(nèi)存,
敏感詞越多,內(nèi)存占用的就越大。
③第三種在這里要特別說明一下,那就是你自己去寫一個算法吧,或者在現(xiàn)有的算法的基礎上去優(yōu)化,這也是小Alan追求的至
高境界之一,如果哪位淫兄有自己的想法一定別忘了小Alan,可以加小Alan的QQ:810104041教小Alan兩招耍耍。
二、代碼實現(xiàn)
其目錄結(jié)構(gòu)如下:
其中resources資源目錄中:
stopwd.txt :停頓詞,匹配時間直接過濾。
wd.txt:敏感詞庫。
1、WordFilter敏感詞過濾類
package org.andy.sensitivewdfilter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.andy.sensitivewdfilter.util.BCConvert; /** * 創(chuàng)建時間:2016年8月30日 下午3:01:12 * * 思路: 創(chuàng)建一個FilterSet,枚舉了0~65535的所有char是否是某個敏感詞開頭的狀態(tài) * * 判斷是否是 敏感詞開頭 | | 是 不是 獲取頭節(jié)點 OK--下一個字 然后逐級遍歷,DFA算法 * * @author andy * @version 2.2 */ public class WordFilter { private static final FilterSet set = new FilterSet(); // 存儲首字 private static final Map<Integer, WordNode> nodes = new HashMap<Integer, WordNode>(1024, 1); // 存儲節(jié)點 private static final Set<Integer> stopwdSet = new HashSet<>(); // 停頓詞 private static final char SIGN = '*'; // 敏感詞過濾替換 static { try { long a = System.nanoTime(); init(); a = System.nanoTime() - a; System.out.println("加載時間 : " + a + "ns"); System.out.println("加載時間 : " + a / 1000000 + "ms"); } catch (Exception e) { throw new RuntimeException("初始化過濾器失敗"); } } private static void init() { // 獲取敏感詞 addSensitiveWord(readWordFromFile("wd.txt")); addStopWord(readWordFromFile("stopwd.txt")); } /** * 增加敏感詞 * @param path * @return */ private static List<String> readWordFromFile(String path) { List<String> words; BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(WordFilter.class.getClassLoader().getResourceAsStream(path))); words = new ArrayList<String>(1200); for (String buf = ""; (buf = br.readLine()) != null;) { if (buf == null || buf.trim().equals("")) continue; words.add(buf); } } catch (Exception e) { throw new RuntimeException(e); } finally { try { if (br != null) br.close(); } catch (IOException e) { } } return words; } /** * 增加停頓詞 * * @param words */ private static void addStopWord(final List<String> words) { if (words != null && words.size() > 0) { char[] chs; for (String curr : words) { chs = curr.toCharArray(); for (char c : chs) { stopwdSet.add(charConvert(c)); } } } } /** * 添加DFA節(jié)點 * @param words */ private static void addSensitiveWord(final List<String> words) { if (words != null && words.size() > 0) { char[] chs; int fchar; int lastIndex; WordNode fnode; // 首字母節(jié)點 for (String curr : words) { chs = curr.toCharArray(); fchar = charConvert(chs[0]); if (!set.contains(fchar)) {// 沒有首字定義 set.add(fchar);// 首字標志位 可重復add,反正判斷了,不重復了 fnode = new WordNode(fchar, chs.length == 1); nodes.put(fchar, fnode); } else { fnode = nodes.get(fchar); if (!fnode.isLast() && chs.length == 1) fnode.setLast(true); } lastIndex = chs.length - 1; for (int i = 1; i < chs.length; i++) { fnode = fnode.addIfNoExist(charConvert(chs[i]), i == lastIndex); } } } } /** * 過濾判斷 將敏感詞轉(zhuǎn)化為成屏蔽詞 * @param src * @return */ public static final String doFilter(final String src) { char[] chs = src.toCharArray(); int length = chs.length; int currc; int k; WordNode node; for (int i = 0; i < length; i++) { currc = charConvert(chs[i]); if (!set.contains(currc)) { continue; } node = nodes.get(currc);// 日 2 if (node == null)// 其實不會發(fā)生,習慣性寫上了 continue; boolean couldMark = false; int markNum = -1; if (node.isLast()) {// 單字匹配(日) couldMark = true; markNum = 0; } // 繼續(xù)匹配(日你/日你妹),以長的優(yōu)先 // 你-3 妹-4 夫-5 k = i; for (; ++k < length;) { int temp = charConvert(chs[k]); if (stopwdSet.contains(temp)) continue; node = node.querySub(temp); if (node == null)// 沒有了 break; if (node.isLast()) { couldMark = true; markNum = k - i;// 3-2 } } if (couldMark) { for (k = 0; k <= markNum; k++) { chs[k + i] = SIGN; } i = i + markNum; } } return new String(chs); } /** * 是否包含敏感詞 * @param src * @return */ public static final boolean isContains(final String src) { char[] chs = src.toCharArray(); int length = chs.length; int currc; int k; WordNode node; for (int i = 0; i < length; i++) { currc = charConvert(chs[i]); if (!set.contains(currc)) { continue; } node = nodes.get(currc);// 日 2 if (node == null)// 其實不會發(fā)生,習慣性寫上了 continue; boolean couldMark = false; if (node.isLast()) {// 單字匹配(日) couldMark = true; } // 繼續(xù)匹配(日你/日你妹),以長的優(yōu)先 // 你-3 妹-4 夫-5 k = i; for (; ++k < length;) { int temp = charConvert(chs[k]); if (stopwdSet.contains(temp)) continue; node = node.querySub(temp); if (node == null)// 沒有了 break; if (node.isLast()) { couldMark = true; } } if (couldMark) { return true; } } return false; } /** * 大寫轉(zhuǎn)化為小寫 全角轉(zhuǎn)化為半角 * * @param src * @return */ private static int charConvert(char src) { int r = BCConvert.qj2bj(src); return (r >= 'A' && r <= 'Z') ? r + 32 : r; } }
其中:
isContains :是否包含敏感詞
doFilter:過濾敏感詞
2、WordNode敏感詞節(jié)點
package org.andy.sensitivewdfilter; import java.util.LinkedList; import java.util.List; /** * 創(chuàng)建時間:2016年8月30日 下午3:07:45 * * @author andy * @version 2.2 */ public class WordNode { private int value; // 節(jié)點名稱 private List<WordNode> subNodes; // 子節(jié)點 private boolean isLast;// 默認false public WordNode(int value) { this.value = value; } public WordNode(int value, boolean isLast) { this.value = value; this.isLast = isLast; } /** * * @param subNode * @return 就是傳入的subNode */ private WordNode addSubNode(final WordNode subNode) { if (subNodes == null) subNodes = new LinkedList<WordNode>(); subNodes.add(subNode); return subNode; } /** * 有就直接返回該子節(jié)點, 沒有就創(chuàng)建添加并返回該子節(jié)點 * * @param value * @return */ public WordNode addIfNoExist(final int value, final boolean isLast) { if (subNodes == null) { return addSubNode(new WordNode(value, isLast)); } for (WordNode subNode : subNodes) { if (subNode.value == value) { if (!subNode.isLast && isLast) subNode.isLast = true; return subNode; } } return addSubNode(new WordNode(value, isLast)); } public WordNode querySub(final int value) { if (subNodes == null) { return null; } for (WordNode subNode : subNodes) { if (subNode.value == value) return subNode; } return null; } public boolean isLast() { return isLast; } public void setLast(boolean isLast) { this.isLast = isLast; } @Override public int hashCode() { return value; } }
三、測試結(jié)果
項目包含敏感詞庫,源碼,停頓詞庫等,只需運行maven打jar包直接可運行。
項目源碼:sensitivewd-filter_jb51.rar
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
JUC循環(huán)屏障CyclicBarrier與CountDownLatch區(qū)別詳解
這篇文章主要為大家介紹了JUC循環(huán)屏障CyclicBarrier與CountDownLatch區(qū)別詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12Java數(shù)據(jù)結(jié)構(gòu)之鏈表詳解
本篇文章我們將講解一種新型的數(shù)據(jù)結(jié)構(gòu)—鏈表,鏈表是一種使用廣泛的通用數(shù)據(jù)結(jié)構(gòu),它可以用來作為實現(xiàn)棧,隊列等數(shù)據(jù)結(jié)構(gòu)的基礎.文中有非常詳細的介紹,需要的朋友可以參考下2021-05-05Spring Boot利用Java Mail實現(xiàn)郵件發(fā)送
這篇文章主要為大家詳細介紹了Spring Boot利用Java Mail實現(xiàn)郵件發(fā)送,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-02-02Java中Object toString方法簡介_動力節(jié)點Java學院整理
Object類在Java里面是一個比較特殊的類,JAVA為了組織這個類組織得比較方便,它提供了一個最根上的類,相當于所有的類都是從這個類繼承,這個類就叫Object。接下來通過本文給大家介紹Object toString方法,需要的的朋友參考下吧2017-05-05