Java使用DFA算法实现敏感词过滤
创始人
2024-05-27 16:34:22
0

1 前言

敏感词过滤就是你在项目中输入某些字(比如输入xxoo相关的文字时)时要能检测出来,很多项目中都会有一个敏感词管理模块,在敏感词管理模块中你可以加入敏感词,然后根据加入的敏感词去过滤输入内容中的敏感词并进行相应的处理,要么提示,要么高亮显示,要么直接替换成其它的文字或者符号代替。

敏感词过滤的做法有很多,其中有比较常用的如下几种:

1.查询数据库当中的敏感词,循环每一个敏感词,然后去输入的文本中从头到尾搜索一遍,看是否存在此敏感词,有则做相应的处理,这种方式讲白了就是找到一个处理一个。

优点:so easy。用java代码实现基本没什么难度。

缺点:这效率是非常低的,如果是英文时你会发现一个很无语的事情,比如英文a是敏感词,那我如果是一篇英文文档,那程序它得处理多少次敏感词?谁能告诉我?

2.传说中的DFA算法(有限状态机),也正是我要给大家分享的,毕竟感觉比较通用,算法的原理希望大家能够自己去网上查查

资料,这里就不详细说明了。

优点:至少比上面那sb效率高点。

缺点:对于学过算法的应该不难,对于没学过算法的用起来也不难,就是理解起来有点gg疼,匹配效率也不高,比较耗费内存,

敏感词越多,内存占用的就越大。

2 代码实现

2.1 敏感词库初始化

在项目启动前读取数据,将敏感词加载到Map中,具体实现如下:

建表语句:

CREATE TABLE `sensitive_word` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`content` varchar(50) NOT NULL COMMENT '关键词',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',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');

实体类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;}

数据库持久类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 {
}

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;/*** 敏感词过滤服务类* @author wuKeFan* @date 2023-03-02 13:47:04*/
public interface SensitiveWordService extends IService {Set 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 implements SensitiveWordService{@Overridepublic Set sensitiveWordFiltering(String text) {// 得到敏感词有哪些,传入2表示获取所有敏感词return SensitiveWordUtils.getSensitiveWord(text, 2);}
}

敏感词过滤工具类SensitiveWordUtils:

package com.wkf.workrecord.tools.dfa;import com.wkf.workrecord.tools.dfa.entity.SensitiveWord;
import lombok.extern.slf4j.Slf4j;import java.util.*;/*** 敏感词过滤工具类* @author wuKeFan* @date 2023-03-02 13:45:19*/
@Slf4j
@SuppressWarnings("unused")
public class SensitiveWordUtils {/*** 敏感词库*/public static final Map sensitiveWordMap = new HashMap<>();/*** 只过滤最小敏感词*/public static int minMatchTYpe = 1;/*** 过滤所有敏感词*/public static int maxMatchType = 2;/*** 初始化敏感词*/public static void initKeyWord(List sensitiveWords) {try {// 从敏感词集合对象中取出敏感词并封装到Set集合中Set keyWordSet = new HashSet<>();for (SensitiveWord s : sensitiveWords) {keyWordSet.add(s.getContent().trim());}// 将敏感词库加入到HashMap中addSensitiveWordToHashMap(keyWordSet);}catch (Exception e) {log.error("初始化敏感词出错,", e);}}/*** 封装敏感词库** @param keyWordSet 敏感词库列表*/private static void addSensitiveWordToHashMap(Set keyWordSet) {// 敏感词String key;// 用来按照相应的格式保存敏感词库数据Map nowMap;// 用来辅助构建敏感词库Map newWorMap;// 使用一个迭代器来循环敏感词集合for (String s : keyWordSet) {key = s;// 等于敏感词库,HashMap对象在内存中占用的是同一个地址,所以此nowMap对象的变化,sensitiveWordMap对象也会跟着改变nowMap = sensitiveWordMap;for (int i = 0; i < key.length(); i++) {// 截取敏感词当中的字,在敏感词库中字为HashMap对象的Key键值char keyChar = key.charAt(i);// 判断这个字是否存在于敏感词库中Object wordMap = nowMap.get(keyChar);if (wordMap != null) {nowMap = (Map) wordMap;} else {newWorMap = new HashMap<>();newWorMap.put("isEnd", "0");nowMap.put(keyChar, newWorMap);nowMap = newWorMap;}// 如果该字是当前敏感词的最后一个字,则标识为结尾字if (i == key.length() - 1) {nowMap.put("isEnd", "1");}log.info("封装敏感词库过程:" + sensitiveWordMap);}log.info("查看敏感词库数据:" + sensitiveWordMap);}}/*** 敏感词库敏感词数量** @return 返回数量*/public static int getWordSize() {return SensitiveWordUtils.sensitiveWordMap.size();}/*** 是否包含敏感词** @param txt 敏感词* @param matchType 匹配类型* @return 返回结果*/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;}/*** 获取敏感词内容** @param txt 敏感词* @param matchType 匹配类型* @return 敏感词内容*/public static Set getSensitiveWord(String txt, int matchType) {Set sensitiveWordList = new HashSet<>();for (int i = 0; i < txt.length(); i++) {int length = checkSensitiveWord(txt, i, matchType);if (length > 0) {// 将检测出的敏感词保存到集合中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 set = getSensitiveWord(txt, matchType);Iterator 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;}/*** 替换敏感词内容** @param replaceChar 需要替换的敏感词* @param length 替换长度* @return 返回结果*/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();}/*** 检查敏感词数量** @param txt 敏感词* @param beginIndex 开始下标* @param matchType 匹配类型* @return 返回数量*/public static int checkSensitiveWord(String txt, int beginIndex, int matchType) {boolean flag = false;// 记录敏感词数量int matchFlag = 0;char word;Map nowMap = SensitiveWordUtils.sensitiveWordMap;for (int i = beginIndex; i < txt.length(); i++) {word = txt.charAt(i);// 判断该字是否存在于敏感词库中nowMap = (Map) nowMap.get(word);if (nowMap != null) {matchFlag++;// 判断是否是敏感词的结尾字,如果是结尾字则判断是否继续检测if ("1".equals(nowMap.get("isEnd"))) {flag = true;// 判断过滤类型,如果是小过滤则跳出循环,否则继续循环if (SensitiveWordUtils.minMatchTYpe == matchType) {break;}}}else {break;}}if (!flag) {matchFlag = 0;}return matchFlag;}}

项目启动完成后执行初始化敏感关键字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;/*** 初始化敏感关键字* @author wuKeFan* @date 2023-03-02 13:57:45*/
@Component
public class StartInit {@Resourceprivate SensitiveWordMapper sensitiveWordMapper;@PostConstructpublic void init() {// 从数据库中获取敏感词对象集合(调用的方法来自Dao层,此方法是service层的实现类)List sensitiveWords = sensitiveWordMapper.selectList(new QueryWrapper<>());// 构建敏感词库SensitiveWordUtils.initKeyWord(sensitiveWords);}}

2.2 编写测试类

编写测试脚本测试效果.代码如下:

    @Testpublic void sensitiveWordTest() {Set set = sensitiveWordService.sensitiveWordFiltering("吴名氏到此一游");for (String string : set) {System.out.println(string);}}

执行结果如下:

吴名氏为敏感词,匹配成功

相关内容

热门资讯

安卓9系统怎样应用分身,轻松实... 你有没有发现,手机里的APP越来越多,有时候一个APP里还要处理好多任务,分身功能简直就是救星啊!今...
获取安卓系统的ip地址,轻松获... 你有没有想过,你的安卓手机里隐藏着一个神秘的IP地址?没错,就是那个能让你在网络世界里找到自己的小秘...
LG彩电安卓系统升级,畅享智能... 你家的LG彩电是不是最近有点儿“闹别扭”,屏幕上时不时地跳出个升级提示?别急,今天就来给你详细说说这...
阴阳师安卓苹果系统,安卓与苹果... 亲爱的玩家们,你是否曾在深夜里,手握手机,沉浸在阴阳师的神秘世界?今天,就让我带你一起探索这款风靡全...
华为安卓系统区别在哪,独特创新... 你知道吗?最近手机圈里可是热闹非凡,尤其是华为的新动作,让很多人眼睛都瞪大了。没错,我说的就是华为自...
怎么重新刷安卓手机系统,深度解... 手机用久了,是不是感觉卡顿得厉害?别急,今天就来教你怎么重新刷安卓手机系统,让你的手机焕然一新,速度...
刷正版安卓系统教程,刷正版安卓... 你有没有想过,让你的安卓手机焕然一新,体验一把正版系统的魅力呢?别急,今天就来手把手教你如何刷正版安...
移动支撑系统安卓版,助力移动办... 你有没有发现,现在的生活越来越离不开手机了?无论是工作还是娱乐,手机几乎成了我们生活的必需品。而今天...
安卓怎么进win系统界面,安卓... 亲爱的安卓用户,你是否曾幻想过在安卓设备上直接体验Windows系统的魅力?别再羡慕那些Window...
incall可以升级安卓系统吗... 你有没有想过,你的手机是不是也能像电脑一样,时不时地来个系统升级呢?今天,咱们就来聊聊这个话题——i...
安卓系统带农历软件,尽享传统节... 你知道吗?现在智能手机上有个特别实用的功能,那就是农历显示。对于咱们中国人来说,农历可是有着深厚的历...
安卓系统资源占用高,揭秘原因与... 你有没有发现,你的安卓手机最近变得越来越慢了?是不是觉得打开一个应用都要等半天,甚至有时候还会卡死?...
安卓10的系统有哪些,功能升级... 你有没有发现,你的安卓手机最近是不是变得有点不一样了?没错,就是那个神秘的安卓10系统!它就像一位魔...
固态硬盘系统迁移到安卓,固态硬... 你有没有想过,把你的固态硬盘系统迁移到安卓设备上,是不是能让你在移动办公或者娱乐时更加得心应手呢?想...
平板电脑能玩安卓系统吗,畅享丰... 你有没有想过,平板电脑竟然也能玩安卓系统?这可不是天方夜谭,而是科技发展的新趋势。想象你手中的平板瞬...
安卓刷精简系统下载,轻松打造高... 你有没有想过,你的安卓手机是不是有点儿“臃肿”了呢?运行速度慢,电池续航短,有时候还卡得要命。别急,...
安卓子系统windows11,... 你知道吗?最近科技圈可是炸开了锅,因为安卓子系统在Windows 11上的兼容性成了大家热议的话题。...
电脑里怎么下载安卓系统,电脑端... 你有没有想过,你的电脑里也能装上安卓系统呢?没错,就是那个让你手机不离手的安卓!今天,就让我来带你一...
索尼相机魔改安卓系统,魔改系统... 你知道吗?最近在摄影圈里掀起了一股热潮,那就是索尼相机魔改安卓系统。这可不是一般的改装,而是让这些专...
安卓系统哪家的最流畅,安卓系统... 你有没有想过,为什么你的手机有时候像蜗牛一样慢吞吞的,而别人的手机却能像风一样快?这背后,其实就是安...