• 网上过滤敏感词工具类有的存在挺多bug,这是我自己改用的过滤敏感词工具类,目前来说没啥bug,如果有bug欢迎在评论指出
  • 使用前缀树 Trie 实现的过滤敏感词,树节点用静态内部类表示了,都写在一个 SensitiveFilter 一个文件里了
package top.linzeliang.util;

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map; /**
* 敏感词过滤
*
* @Author: linzeliang
* @Date: 2021/12/8
*/
@Component
public class SensitiveFilter { private static final Logger LOGGER = LoggerFactory.getLogger(SensitiveFilter.class); /**
* 替换符
*/
private static final String REPLACEMENT = "*"; /**
* 根节点,根节点是不带值的
*/
private final TrieNode ROOT_NODE = new TrieNode(); /**
* 初始化前缀树,读取敏感词文件构造前缀树
*
* @date 2021/12/9
*/
@PostConstruct
private void init() {
try (
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))
) {
String keyword;
// 每次读取一行
while ((keyword = reader.readLine()) != null) {
// 添加到前缀树
this.addKeyword(keyword);
}
} catch (IOException e) {
LOGGER.error("加载敏感词文件失败: " + e.getMessage());
}
} /**
* 将一个敏感词添加到前缀树中
*
* @param keyword 敏感词
* @date 2021/12/9
*/
private void addKeyword(String keyword) {
TrieNode tempNode = ROOT_NODE;
for (int i = 0; i < keyword.length(); i++) {
//获取单个字符
char c = keyword.charAt(i);
// 先查询是否存在,就是是否有这个开头的敏感词
TrieNode subNode = tempNode.getSubNode(c); // 如果子节点中不存在,就新建,并且添加到tempNode的子节点
if (null == subNode) {
subNode = new TrieNode();
tempNode.addSubNodes(c, subNode);
} // 标记一下最后一个节点,即叶子节点
if (i == keyword.length() - 1) {
subNode.setKeywordEnd(true);
} // 将指针指向子节点
tempNode = subNode;
}
} /**
* 过滤敏感词
*
* @param text 待过滤文本
* @return java.lang.String
* @date 2021/12/9
*/
public String filter(String text) {
// 过滤文本为空返回 null
if (StringUtils.isBlank(text)) {
return null;
} // 指针1,刚开始指向根节点
TrieNode tempNode = ROOT_NODE;
// 指针2
int start = 0;
// 指针3
int end = 0;
// 过滤结果
StringBuilder sb = new StringBuilder(); // 当指针3未到字符串末尾时,都进行过滤
while (end < text.length()) {
// 获取待过滤的每个字符
char c = text.charAt(end); // 如果是无效符号就跳过
if (isSymbol(c) && end != text.length() - 1) {
// 若指针1处于根节点,就将此符号计入结果,让指针2向下走一步
if (tempNode == ROOT_NODE) {
sb.append(c);
start++;
}
// 无论符号在开头或中间,指针3都向下走一步
end++;
continue;
} // 查看敏感字符对应的子节点是否存在
tempNode = tempNode.getSubNode(c);
// 如果没有敏感词对应的子节点,说明不包含,因此跳过这个字符
if (tempNode == null) {
// 以begin开头的字符串不是敏感词
sb.append(text.charAt(start));
// start 和 begin 都进入下一个位置
end = ++start;
// 重新指向根节点
tempNode = ROOT_NODE;
} else if (tempNode.isKeywordEnd()) {
// 遇到敏感词结束标识,即发现敏感词,将begin~position字符串替换掉
for (int i = start; i <= end; i++) {
sb.append(REPLACEMENT);
}
// 进入下一个位置
start = ++end;
// 重新指向根节点
tempNode = ROOT_NODE;
} else {
// 如果找到了敏感字符,但是又没结束,因此继续检查下一个字符
// 如果当前 start 字符到 end 末尾字符没有识别出敏感词,那么就从 start 的下一个开始进行查找
if (end < text.length() - 1) {
end++;
} else {
// 这里还是指向 start,并没有加 1,因为下一步循环就进入到 tempNode == null 判断里面了
// 因此 start 和 end 都会加 1,同时上一个字符也会被加入到sb中
end = start;
}
}
} // 将最后一批字符计入结果
sb.append(text.substring(start)); return sb.toString();
} /**
* 判断是否为符号
*
* @param c 待判断符号
* @return boolean
* @date 2021/12/9
*/
private boolean isSymbol(Character c) {
// 0x2E80~0x9FFF 是东亚文字范围
return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
} /**
* 前缀树节点
* 因为不需要用到外部类SensitiveFilter,所以设置成静态的就行,能提高性能
*/
private static class TrieNode { /**
* 关键词结束标识符
*/
private boolean isKeywordEnd; /**
* 存放子节点
* 因为子节点集合是固定的,只会往这个集合增删元素,而不会改变这个集合指针指向,所以使用final
*/
private final Map<Character, TrieNode> subNodes; public TrieNode() {
this.isKeywordEnd = false;
this.subNodes = new HashMap<>();
} public boolean isKeywordEnd() {
return isKeywordEnd;
} public void setKeywordEnd(boolean keywordEnd) {
isKeywordEnd = keywordEnd;
} /**
* 添加子节点
*
* @param c 节点名称
* @param node 节点
* @date 2021/12/9
*/
public void addSubNodes(Character c, TrieNode node) {
subNodes.put(c, node);
} /**
* 获取子节点
*
* @param c 查询的字符
* @return top.linzeliang.community.util.SensitiveFilter.TrieNode
* @date 2021/12/9
*/
public TrieNode getSubNode(Character c) {
return subNodes.get(c);
}
}
}

过滤敏感词工具类SensitiveFilter的更多相关文章

  1. Java 敏感词过滤,Java 敏感词替换,Java 敏感词工具类

    Java 敏感词过滤,Java 敏感词替换,Java 敏感词工具类   =========================== ©Copyright 蕃薯耀 2017年9月25日 http://www ...

  2. 【SpringBoot】前缀树 Trie 过滤敏感词

    1.过滤敏感词 Spring Boot实践,开发社区核心功能 完成过滤敏感词 Trie 名称:Trie也叫做字典树.前缀树(Prefix Tree).单词查找树 特点:查找效率高,消耗内存大 应用:字 ...

  3. SpringBoot开发十四-过滤敏感词

    项目需求-过滤敏感词 利用 Tire 树实现过滤敏感词 定义前缀树,根据敏感词初始化前缀树,编写过滤敏感词的方法 代码实现 我们首先把敏感词存到一个文件 sensitive.txt: 赌博 嫖娼 吸毒 ...

  4. web前端js过滤敏感词

    web前端js过滤敏感词 这里是用文本输入框还有文本域绑定了失去焦点事件,然后再遍历敏感词数组进行匹配和替换. var keywords=["阿扁","呵呵", ...

  5. (转)两种高效过滤敏感词算法--DFA算法和AC自动机算法

    原文:https://blog.csdn.net/u013421629/article/details/83178970 一道bat面试题:快速替换10亿条标题中的5万个敏感词,有哪些解决思路? 有十 ...

  6. PHP 扩展 trie-tree, swoole过滤敏感词方案

    在一些app,web中评论以及一些文章会看到一些*等,除了特定的不显示外,我们会把用户输入的一些敏感字符做处理,具体显示为*还是其他字符按照业务区实现. 下面简单介绍下业务处理. 原文地址:小时刻个人 ...

  7. php过滤敏感词

    <?php /**  * 敏感词过滤工具类  * 使用方法  * echo FilterTools::filterContent("你妈的我操一色狼杂种二山食物"," ...

  8. [转]Filter实现处理中文乱码,转义html标签,过滤敏感词

    原文地址:http://www.cnblogs.com/xdp-gacl/p/3952405.html 在filter中可以得到代表用户请求和响应的request.response对象,因此在编程中可 ...

  9. js 过滤敏感词 ,可将带有标点符号的敏感词过滤掉

    function transSensitive(content) { // var Sensitive = H.getStorage("Sensitive");//敏感词数组 va ...

随机推荐

  1. 求求你们了,别再写满屏的 if/ else 了!

    为什么我们写的代码都是 if-else? 程序员想必都经历过这样的场景:刚开始自己写的代码很简洁,逻辑清晰,函数精简,没有一个 if-else,可随着代码逻辑不断完善和业务的瞬息万变:比如需要对入参进 ...

  2. OpenShift 本地开发环境配置(基于 Minishift)

    本文要做什么? 很多为了验证应用在 OpenShift 平台的行为是否正常,或者组成一个简单的开发环境,直接搭建一个 OpenShift/Origin 环境可能太重了,而且运行在本机可能占用内存也太多 ...

  3. Javascript复制内容到剪贴板,解决navigator.clipboard Cannot read property 'writeText' of undefined

    起因 最近帮同事实现了一个小功能--复制文本到剪贴板,主要参考了前端大神阮一峰的博客,根据 navigator.clipboard 返回的 Clipboard 对象的方法 writeText() 写文 ...

  4. [hdu7023]Yet Another Matrix Problem

    关于$f(x)$的条件,将$C=A\times B$代入,即$\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{k=1}^{r}A_{i,k}B_{k,j}=x$ 调换枚举顺序,即$ ...

  5. [atAGC029F]Construction of a tree

    构造一张二分图,左边是$n$个点,右边是$n-1$个集合,按照点属于集合连边 定义一组匹配的意义,即说明该点的父亲在该集合中选择 利用dinic求出二分图的最大匹配,若不为$n-1$则无解,否则考虑如 ...

  6. mysql密码忘记如何恢复(windows/liunx版本:mysql-8.0.27)

    windows/liunx版本:mysql-8.0.27参考官网文档: https://dev.mysql.com/doc/refman/8.0/en/resetting-permissions.ht ...

  7. Linux终端使用aplay播放wav

    Linux终端使用aplay播放wav aplay是一个ALSA的声卡命令行soundfile录音机的驱动程序. 在linux下可以使用下面命令来查看用法: man aplay 所以可以使用来播放.w ...

  8. CF1610F F. Mashtali: a Space Oddysey

    我们首先发现有如下性质: 我们不妨先随机定向边,那么我们发现无论我们如何翻转边. 都会对其两端的点,造成 \(2 / 4\) 的影响,所以我们发现如果一个点其和他相连的所有边权和为偶数,则我们不能调整 ...

  9. 洛谷 P5471 - [NOI2019] 弹跳(二维线段树优化建图+堆优化存边)

    题面传送门 一道非常有意思的题(大概可以这么形容?) 首先看到这类一个点想一个区域内连边的题目可以很自然地想到线段树优化建图,只不过这道题是二维的,因此需要使用二维线段树优化建图,具体来说,我们外层开 ...

  10. R语言与医学统计图形-【25】ggplot图形分面

    ggplot2绘图系统--图形分面 ggplot2的分面faceting,主要有三个函数: facet_grid facet_wrap facet_null (不分面) 1. facet_grid函数 ...