Java使用DFA算法实现敏感词过滤
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<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;/*** 敏感词过滤服务类* @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{@Overridepublic Set<String> 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<Object, Object> sensitiveWordMap = new HashMap<>();/*** 只过滤最小敏感词*/public static int minMatchTYpe = 1;/*** 过滤所有敏感词*/public static int maxMatchType = 2;/*** 初始化敏感词*/public static void initKeyWord(List<SensitiveWord> sensitiveWords) {try {// 从敏感词集合对象中取出敏感词并封装到Set集合中Set<String> 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<String> keyWordSet) {// 敏感词String key;// 用来按照相应的格式保存敏感词库数据Map<Object, Object> nowMap;// 用来辅助构建敏感词库Map<Object, Object> 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<Object, Object>) 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<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) {// 将检测出的敏感词保存到集合中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;}/*** 替换敏感词内容** @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<Object, Object> nowMap = SensitiveWordUtils.sensitiveWordMap;for (int i = beginIndex; i < txt.length(); i++) {word = txt.charAt(i);// 判断该字是否存在于敏感词库中nowMap = (Map<Object, Object>) 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<SensitiveWord> sensitiveWords = sensitiveWordMapper.selectList(new QueryWrapper<>());// 构建敏感词库SensitiveWordUtils.initKeyWord(sensitiveWords);}}
2.2 编写测试类
编写测试脚本测试效果.代码如下:
@Testpublic void sensitiveWordTest() {Set<String> set = sensitiveWordService.sensitiveWordFiltering("吴名氏到此一游");for (String string : set) {System.out.println(string);}}
执行结果如下:

吴名氏为敏感词,匹配成功
相关文章:

Java使用DFA算法实现敏感词过滤
1 前言敏感词过滤就是你在项目中输入某些字(比如输入xxoo相关的文字时)时要能检测出来,很多项目中都会有一个敏感词管理模块,在敏感词管理模块中你可以加入敏感词,然后根据加入的敏感词去过滤输入内容中的敏感词并进行…...
UG NX二次开发(C#)-外挂 - 配置文件说明(.men文件/.rtb文件/.trb文件)
文章目录 1、前言2、UG NX菜单说明2.1UG NX的Ribbon样式说明2.2 UG NX的Ribbon配置文件3、外挂的加载配置文件说明3.1 创建配置文件夹3.2 填写.men文件3.2 填写.rtb文件3.2 填写.tbr文件4、将外挂加载到UG NX菜单中5、重启UG NX,就可以实现外挂加载了。1、前言 UG NX二次开发…...

Web3中文|日本元宇宙经济“狂飙”
2月27日,三菱、富士通和其它科技公司发布关于建立“日本元宇宙经济区”的协议,表示将联手从角色扮演游戏的角度创建开放的元宇宙基础设施,以推动日本的Web3战略。据了解,日本一直在努力将Web3技术纳入其国家议程,去年1…...

@Autowired和@Resource到底有什么区别
Autowired 和 Resource 都是 Spring/Spring Boot 项目中,用来进行依赖注入的注解。它们都提供了将依赖对象注入到当前对象的功能,但二者却有众多不同,并且这也是常见的面试题之一,所以我们今天就来盘它。 Autowired 和 Resource 的…...

2023年最新阿里云服务器价格表出炉(精准收费标准及配置价格表)
阿里云在全球率先宣布了基于 Intel Ice Lake 处理器的第七代云服务器ECS,性能提升的同时降低了报价,性价比更高了。进入2023年阿里云服务器价格依然是大家关心的问题,事实上阿里云服务器租用价格和最新收费标准都可以通过官方云服务器计算器来…...
ElasticSearch - SpringBoot整合ES实现文档的增删改操作
文章目录1. ElasticSearch和kibana的安装和配置2. SpringBoot 项目环境搭建3. 创建索引4. 索引文档5. 更新文档6. 删除文档https://www.elastic.co/guide/en/elasticsearch/reference/current/search-your-data.htmlhttps://www.elastic.co/guide/cn/elasticsearch/guide/curre…...

嵌入式 LVGL移植到STM32F4
目录 LVGL简介 1、特点 2、LVGL的硬件要求 3、相关网站 4、LVGL源码下载 5、LVGL移植要求 5.1 移植过程-添加源码 2、更改接口文件 3、显示实现 4、添加外部中文字体的方法 5、编译下载后有几种情况 6、调用显示 6、GUI-Guider使用 6.1 安装软件 6.2 使用…...

VSCode——SSH免密登录
文章目录本地PC端(一般为Windows)1. 检查自己是否已经生成公钥2. 配置VScode的SSH config远程服务器端1. 服务器新建授权文件2. 赋权限3. 重启远程服务器的ssh服务最全步骤:【设置ssh免密不起作用?彻底搞懂密钥】vscode在remote S…...

python未来应用前景怎么样
Python近段时间一直涨势迅猛,在各大编程排行榜中崭露头角,得益于它多功能性和简单易上手的特性,让它可以在很多不同的工作中发挥重大作用。 正因如此,目前几乎所有大中型互联网企业都在使用 Python 完成各种各样的工作࿰…...

webpack基本使用和开发环境配置
目录 1 webpack 基本使用 01 webpack 简介 02 webpack 初体验 2 webpack开发环境配置 03 打包样式资源 04 打包html资源 05 打包图片资源 06 打包其他资源(以打包icon为例) 07 devServer 08.开发环境配置 1 webpack 基本使用 由于笔记文档没有…...

3.2 http协议
一.HTTP协议1.概述是计算机网络的核心概念,是一种网络协议网络协议种类非常多,其中IP,TCP,UDP...其中还有一个应用非常广泛的协议.HTTPHTTP协议是日常开发中用的最多的协议HTTP处在TCP/IP五层协议栈的应用层HTTP在传输层是基于TCP的,(http/1 HTTP/2是基于TCP,最新版本的HTTP/3是…...

页面访问升级出错怎么解决
相信大家在访问网站的时候时常会遇到页面访问界面升级,暂时不可能进行访问操作,可能遇到这种情况很多小伙伴们都不知道怎么版,其实互联网网页在正常使用过程中是不会出现这种问题的。那么如果遇到页面访问界面升级怎么办?页面访问界面升级通…...
leetcode 181. 超过经理收入的员工
SQL架构 表:Employee ---------------------- | Column Name | Type | ---------------------- | id | int | | name | varchar | | salary | int | | managerId | int | ---------------------- Id是该表的主键。 该表的…...

任务类风险漏洞挖掘思路
任务类风险定义: 大部分游戏都离不开任务,游戏往往也会借助任务,来引导玩家上手,了解游戏背景,增加游戏玩法,提升游戏趣味性。任务就像线索,将游戏的各个章节,各种玩法,…...

2023年Dubbo常见面试题
2023年Dubbo常见面试题 Dubbo 中 zookeeper 做注册中心,如果注册中心集群都挂掉,发布者和订阅者之间还能通信么? 可以通信的,启动 dubbo 时,消费者会从 zk 拉取注册的生产者的地址接口等数据,缓存在本地。…...

星光2开发板使用ECR6600U无线wifi网卡的方法
visionfive2 开发板性能还是不错的,有些人买的时候会带一个无线wifi网卡,但是官方提供的操作系统没有驱动。 所以需要自己编驱动(他大爷的)。 还好有人已经踩过坑了。 星光2之USB无线网卡使用教程【新增RTL8832AU WiFi6双频无线…...

【ArcGIS Pro二次开发】(11):面要素的一键拓扑
在工作中,经常需要对要素进行拓扑检查。 在ArcGIS Pro中正常的工作流程是在数据库中【新建要素数据集——新建拓扑——将要素加入拓扑——添加规则——验证】,工作流程不算短,操作起来比较繁琐。 下面以一个例子演示如何在ArcGIS Pro SDK二次…...

【实现点击下载按钮功能 Objective-C语言】
一、实现点击下载按钮功能, 1.接下来,我们再实现另外一个功能,是什么,点击下载按钮吧: 点击下载按钮,是不是要有效果啊, 就是给大家实现这个功能, 首先,我们要实现单击这个效果,是不是要给按钮注册单击事件吧, 请问,这个按钮在哪里啊,是在控制器里面吗,不是,…...

界面控件DevExpress WinForm——轻松构建类Visual Studio UI(三)
DevExpress WinForm拥有180组件和UI库,能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForm能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜任…...

项目实战-瑞吉外卖day01(B站)
瑞吉外卖-Day01课程内容软件开发整体介绍瑞吉外卖项目介绍开发环境搭建后台登录功能开发后台退出功能开发1. 软件开发整体介绍作为一名软件开发工程师,我们需要了解在软件开发过程中的开发流程, 以及软件开发过程中涉及到的岗位角色,角色的分工、职责&am…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...

【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...

论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...

C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...

Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...