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 編寫(xiě)測(cè)試類
編寫(xiě)測(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-12
Java面向?qū)ο蠡A(chǔ)知識(shí)之?dāng)?shù)組和鏈表
這篇文章主要介紹了Java面向?qū)ο蟮闹當(dāng)?shù)組和鏈表,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好的幫助,需要的朋友可以參考下2021-11-11
Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹(shù)的真正理解
這篇文章主要為大家詳細(xì)介紹了Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹(shù)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
Java并發(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-12
SpringBoot + 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-07
mybatis 如何利用resultMap復(fù)雜類型list映射
這篇文章主要介紹了mybatis 如何利用resultMap復(fù)雜類型list映射的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07

