Java使用DFA算法實(shí)現(xiàn)敏感詞過(guò)濾的示例代碼
1 前言
敏感詞過(guò)濾就是你在項(xiàng)目中輸入某些字(比如輸入xxoo相關(guān)的文字時(shí))時(shí)要能檢測(cè)出來(lái),很多項(xiàng)目中都會(huì)有一個(gè)敏感詞管理模塊,在敏感詞管理模塊中你可以加入敏感詞,然后根據(jù)加入的敏感詞去過(guò)濾輸入內(nèi)容中的敏感詞并進(jìn)行相應(yīng)的處理,要么提示,要么高亮顯示,要么直接替換成其它的文字或者符號(hào)代替。
敏感詞過(guò)濾的做法有很多,其中有比較常用的如下幾種:
1.查詢數(shù)據(jù)庫(kù)當(dāng)中的敏感詞,循環(huán)每一個(gè)敏感詞,然后去輸入的文本中從頭到尾搜索一遍,看是否存在此敏感詞,有則做相應(yīng)的處理,這種方式講白了就是找到一個(gè)處理一個(gè)。
優(yōu)點(diǎn):so easy。用java代碼實(shí)現(xiàn)基本沒(méi)什么難度。
缺點(diǎn):這效率是非常低的,如果是英文時(shí)你會(huì)發(fā)現(xiàn)一個(gè)很無(wú)語(yǔ)的事情,比如英文a是敏感詞,那我如果是一篇英文文檔,那程序它得處理多少次敏感詞?誰(shuí)能告訴我?
2.傳說(shuō)中的DFA算法(有限狀態(tài)機(jī)),也正是我要給大家分享的,畢竟感覺(jué)比較通用,算法的原理希望大家能夠自己去網(wǎng)上查查
資料,這里就不詳細(xì)說(shuō)明了。
優(yōu)點(diǎn):至少比上面那sb效率高點(diǎn)。
缺點(diǎn):對(duì)于學(xué)過(guò)算法的應(yīng)該不難,對(duì)于沒(méi)學(xué)過(guò)算法的用起來(lái)也不難,就是理解起來(lái)有點(diǎn)gg疼,匹配效率也不高,比較耗費(fèi)內(nèi)存,
敏感詞越多,內(nèi)存占用的就越大。
2 代碼實(shí)現(xiàn)
2.1 敏感詞庫(kù)初始化
在項(xiàng)目啟動(dòng)前讀取數(shù)據(jù),將敏感詞加載到Map中,具體實(shí)現(xiàn)如下:
建表語(yǔ)句:
CREATE TABLE `sensitive_word` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `content` varchar(50) NOT NULL COMMENT '關(guān)鍵詞', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; INSERT INTO `fuying`.`sensitive_word` (`id`, `content`, `create_time`, `update_time`) VALUES (1, '吳名氏', '2023-03-02 14:21:36', '2023-03-02 14:21:36');
實(shí)體類SensitiveWord.java:
package com.wkf.workrecord.tools.dfa.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * @author wuKeFan * @date 2023-03-02 13:48:58 */ @Data @TableName("sensitive_word") public class SensitiveWord implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Integer id; private String content; private Date createTime; private Date updateTime; }
數(shù)據(jù)庫(kù)持久類SensitiveWordMapper.java:
package com.wkf.workrecord.tools.dfa.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.wkf.workrecord.tools.dfa.entity.SensitiveWord; /** * @author wuKeFan * @date 2023-03-02 13:50:16 */ public interface SensitiveWordMapper extends BaseMapper<SensitiveWord> { }
service類SensitiveWordService.java和SensitiveWordServiceImpl.java:
package com.wkf.workrecord.tools.dfa.service; import com.baomidou.mybatisplus.extension.service.IService; import com.wkf.workrecord.tools.dfa.entity.SensitiveWord; import java.util.Set; /** * 敏感詞過(guò)濾服務(wù)類 * @author wuKeFan * @date 2023-03-02 13:47:04 */ public interface SensitiveWordService extends IService<SensitiveWord> { Set<String> sensitiveWordFiltering(String text); }
package com.wkf.workrecord.tools.dfa.service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.wkf.workrecord.tools.dfa.mapper.SensitiveWordMapper; import com.wkf.workrecord.tools.dfa.SensitiveWordUtils; import com.wkf.workrecord.tools.dfa.entity.SensitiveWord; import org.springframework.stereotype.Service; import java.util.Set; /** * @author wuKeFan * @date 2023-03-02 13:48:04 */ @Service public class SensitiveWordServiceImpl extends ServiceImpl<SensitiveWordMapper, SensitiveWord> implements SensitiveWordService{ @Override public Set<String> sensitiveWordFiltering(String text) { // 得到敏感詞有哪些,傳入2表示獲取所有敏感詞 return SensitiveWordUtils.getSensitiveWord(text, 2); } }
敏感詞過(guò)濾工具類SensitiveWordUtils:
package com.wkf.workrecord.tools.dfa; import com.wkf.workrecord.tools.dfa.entity.SensitiveWord; import lombok.extern.slf4j.Slf4j; import java.util.*; /** * 敏感詞過(guò)濾工具類 * @author wuKeFan * @date 2023-03-02 13:45:19 */ @Slf4j @SuppressWarnings("unused") public class SensitiveWordUtils { /** * 敏感詞庫(kù) */ public static final Map<Object, Object> sensitiveWordMap = new HashMap<>(); /** * 只過(guò)濾最小敏感詞 */ public static int minMatchTYpe = 1; /** * 過(guò)濾所有敏感詞 */ public static int maxMatchType = 2; /** * 初始化敏感詞 */ public static void initKeyWord(List<SensitiveWord> sensitiveWords) { try { // 從敏感詞集合對(duì)象中取出敏感詞并封裝到Set集合中 Set<String> keyWordSet = new HashSet<>(); for (SensitiveWord s : sensitiveWords) { keyWordSet.add(s.getContent().trim()); } // 將敏感詞庫(kù)加入到HashMap中 addSensitiveWordToHashMap(keyWordSet); } catch (Exception e) { log.error("初始化敏感詞出錯(cuò),", e); } } /** * 封裝敏感詞庫(kù) * * @param keyWordSet 敏感詞庫(kù)列表 */ private static void addSensitiveWordToHashMap(Set<String> keyWordSet) { // 敏感詞 String key; // 用來(lái)按照相應(yīng)的格式保存敏感詞庫(kù)數(shù)據(jù) Map<Object, Object> nowMap; // 用來(lái)輔助構(gòu)建敏感詞庫(kù) Map<Object, Object> newWorMap; // 使用一個(gè)迭代器來(lái)循環(huán)敏感詞集合 for (String s : keyWordSet) { key = s; // 等于敏感詞庫(kù),HashMap對(duì)象在內(nèi)存中占用的是同一個(gè)地址,所以此nowMap對(duì)象的變化,sensitiveWordMap對(duì)象也會(huì)跟著改變 nowMap = sensitiveWordMap; for (int i = 0; i < key.length(); i++) { // 截取敏感詞當(dāng)中的字,在敏感詞庫(kù)中字為HashMap對(duì)象的Key鍵值 char keyChar = key.charAt(i); // 判斷這個(gè)字是否存在于敏感詞庫(kù)中 Object wordMap = nowMap.get(keyChar); if (wordMap != null) { nowMap = (Map<Object, Object>) wordMap; } else { newWorMap = new HashMap<>(); newWorMap.put("isEnd", "0"); nowMap.put(keyChar, newWorMap); nowMap = newWorMap; } // 如果該字是當(dāng)前敏感詞的最后一個(gè)字,則標(biāo)識(shí)為結(jié)尾字 if (i == key.length() - 1) { nowMap.put("isEnd", "1"); } log.info("封裝敏感詞庫(kù)過(guò)程:" + sensitiveWordMap); } log.info("查看敏感詞庫(kù)數(shù)據(jù):" + sensitiveWordMap); } } /** * 敏感詞庫(kù)敏感詞數(shù)量 * * @return 返回?cái)?shù)量 */ public static int getWordSize() { return SensitiveWordUtils.sensitiveWordMap.size(); } /** * 是否包含敏感詞 * * @param txt 敏感詞 * @param matchType 匹配類型 * @return 返回結(jié)果 */ public static boolean isContainSensitiveWord(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; } /** * 獲取敏感詞內(nèi)容 * * @param txt 敏感詞 * @param matchType 匹配類型 * @return 敏感詞內(nèi)容 */ public static Set<String> getSensitiveWord(String txt, int matchType) { Set<String> sensitiveWordList = new HashSet<>(); for (int i = 0; i < txt.length(); i++) { int length = checkSensitiveWord(txt, i, matchType); if (length > 0) { // 將檢測(cè)出的敏感詞保存到集合中 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; String replaceString; while (iterator.hasNext()) { word = iterator.next(); replaceString = getReplaceChars(replaceChar, word.length()); resultTxt = resultTxt.replaceAll(word, replaceString); } return resultTxt; } /** * 替換敏感詞內(nèi)容 * * @param replaceChar 需要替換的敏感詞 * @param length 替換長(zhǎng)度 * @return 返回結(jié)果 */ private static String getReplaceChars(String replaceChar, int length) { StringBuilder resultReplace = new StringBuilder(replaceChar); for (int i = 1; i < length; i++) { resultReplace.append(replaceChar); } return resultReplace.toString(); } /** * 檢查敏感詞數(shù)量 * * @param txt 敏感詞 * @param beginIndex 開(kāi)始下標(biāo) * @param matchType 匹配類型 * @return 返回?cái)?shù)量 */ public static int checkSensitiveWord(String txt, int beginIndex, int matchType) { boolean flag = false; // 記錄敏感詞數(shù)量 int matchFlag = 0; char word; Map<Object, Object> nowMap = SensitiveWordUtils.sensitiveWordMap; for (int i = beginIndex; i < txt.length(); i++) { word = txt.charAt(i); // 判斷該字是否存在于敏感詞庫(kù)中 nowMap = (Map<Object, Object>) nowMap.get(word); if (nowMap != null) { matchFlag++; // 判斷是否是敏感詞的結(jié)尾字,如果是結(jié)尾字則判斷是否繼續(xù)檢測(cè) if ("1".equals(nowMap.get("isEnd"))) { flag = true; // 判斷過(guò)濾類型,如果是小過(guò)濾則跳出循環(huán),否則繼續(xù)循環(huán) if (SensitiveWordUtils.minMatchTYpe == matchType) { break; } } } else { break; } } if (!flag) { matchFlag = 0; } return matchFlag; } }
項(xiàng)目啟動(dòng)完成后執(zhí)行初始化敏感關(guān)鍵字StartInit.java:
package com.wkf.workrecord.tools.dfa; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.wkf.workrecord.tools.dfa.entity.SensitiveWord; import com.wkf.workrecord.tools.dfa.mapper.SensitiveWordMapper; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.List; /** * 初始化敏感關(guān)鍵字 * @author wuKeFan * @date 2023-03-02 13:57:45 */ @Component public class StartInit { @Resource private SensitiveWordMapper sensitiveWordMapper; @PostConstruct public void init() { // 從數(shù)據(jù)庫(kù)中獲取敏感詞對(duì)象集合(調(diào)用的方法來(lái)自Dao層,此方法是service層的實(shí)現(xiàn)類) List<SensitiveWord> sensitiveWords = sensitiveWordMapper.selectList(new QueryWrapper<>()); // 構(gòu)建敏感詞庫(kù) SensitiveWordUtils.initKeyWord(sensitiveWords); } }
2.2 編寫測(cè)試類
編寫測(cè)試腳本測(cè)試效果.代碼如下:
@Test public void sensitiveWordTest() { Set<String> set = sensitiveWordService.sensitiveWordFiltering("吳名氏到此一游"); for (String string : set) { System.out.println(string); } }
執(zhí)行結(jié)果如下:
到此這篇關(guān)于Java使用DFA算法實(shí)現(xiàn)敏感詞過(guò)濾的示例代碼的文章就介紹到這了,更多相關(guān)Java DFA敏感詞過(guò)濾內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- JavaEE Filter敏感詞過(guò)濾的方法實(shí)例詳解
- JavaWeb中的filter過(guò)濾敏感詞匯案例詳解
- java利用DFA算法實(shí)現(xiàn)敏感詞過(guò)濾功能
- Java實(shí)現(xiàn)DFA算法對(duì)敏感詞、廣告詞過(guò)濾功能示例
- Java實(shí)戰(zhàn)之敏感詞過(guò)濾器
- JAVA使用前綴樹(shù)(Tire樹(shù))實(shí)現(xiàn)敏感詞過(guò)濾、詞典搜索
- Java 過(guò)濾器實(shí)現(xiàn)敏感詞匯過(guò)濾功能
- Java數(shù)據(jù)敏感詞轉(zhuǎn)換成符號(hào)的方法詳解
- Java 敏感詞檢測(cè)工具的實(shí)現(xiàn)
相關(guān)文章
IDEA?2022最新激活碼注冊(cè)碼超詳細(xì)教程(親測(cè)激活有效)
這篇文章主要介紹了IDEA?2022最新激活碼超詳細(xì)教程(親測(cè)激活至2099年),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Java面向?qū)ο蠡A(chǔ)知識(shí)之?dāng)?shù)組和鏈表
這篇文章主要介紹了Java面向?qū)ο蟮闹當(dāng)?shù)組和鏈表,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好的幫助,需要的朋友可以參考下2021-11-11Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹(shù)的真正理解
這篇文章主要為大家詳細(xì)介紹了Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹(shù)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Java并發(fā)編程中的synchronized關(guān)鍵字詳細(xì)解讀
這篇文章主要介紹了Java并發(fā)編程中的synchronized關(guān)鍵字詳細(xì)解讀,在Java早期版本中,synchronized 屬于 重量級(jí)鎖,效率低下,這是因?yàn)楸O(jiān)視器鎖(monitor)是依賴于底層的操作系統(tǒng)的Mutex Lock來(lái)實(shí)現(xiàn)的,Java 的線程是映射到操作系統(tǒng)的原生線程之上的,需要的朋友可以參考下2023-12-12SpringBoot + Mybatis增刪改查實(shí)戰(zhàn)記錄
這篇文章主要給大家介紹了關(guān)于SpringBoot + Mybatis增刪改查的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05使用Mybatis的Batch?Insert?Support?實(shí)現(xiàn)批量插入
這篇文章主要介紹了使用Mybatis的Batch?Insert?Support?實(shí)現(xiàn)批量插入。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07mybatis 如何利用resultMap復(fù)雜類型list映射
這篇文章主要介紹了mybatis 如何利用resultMap復(fù)雜類型list映射的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07