spring boot实现违禁词检测
spring boot实现违禁词检测
记录一下使用SpringBoot中使用前缀树对敏感词的一个过滤。
首先呢在resources目录下建立一个文件用来装敏感词例如我在resources/sensitive-words.txt如下:
具体词汇可以直接百度搜索sensitive-words就行了,不能放出来
代码展示:
pom文件
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
util包
SensitiveUtil
package com.nacl.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 HAOYANG
* @create 2023-07-31 14:46
* 敏感词过滤工具类(需要在resource下放置敏感词文件)
*/
@Component
public class SensitiveUtil {
//记录日志
private static final Logger logger = LoggerFactory.getLogger(SensitiveUtil.class);
//敏感词文件
private static final String SENSITIVE_WORD = "sensitive-words.txt";
//用来替代敏感词
private static final String REPLACEMENT = "**";
//前缀树的根节点
private static final TrieNode rootNode = new TrieNode();
// @PostConstruct 表明这是一个初始化方法,在类被实例化的时候就执行。
@PostConstruct
public void init(){
// 获取类加载器,类加载器是从类路径下,去加载资源,所谓的类路径就是target/classes/目录之下。
// 我们的程序一编译,所有的程序都编译到classes下,包括配置文件。
try(
//得到字节流
InputStream is = this.getClass().getClassLoader().getResourceAsStream(SENSITIVE_WORD);
//从字节流中读文字不太方便,把字节流转成字符流,把字符流改成缓冲流会效率更高
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
){
String keyword; //用这个keyword去读数据
while((keyword = reader.readLine()) != null){//一行一行的读
//添加到前缀树
this.addKeyWord(keyword);
}
}catch (IOException e){
logger.error("获取铭感词文件失败:"+e.getMessage());
}
}
//把一个敏感词添加到前缀树中去。
public void addKeyWord(String keyword){
TrieNode tempNode = rootNode;
for(int i = 0 ; i< keyword.length() ; ++i){
char key = keyword.charAt(i);
//找子节点
TrieNode subNode = tempNode.getSubNode(key);
if(subNode == null){//如果没有这个子节点
//初始化子节点;
subNode = new TrieNode();
tempNode.addSubNode(key,subNode);
}
//指向子节点,进入下一次循环
tempNode = subNode;
if(i == keyword.length() -1){
tempNode.setKeyWordEnd(true);
}
}
}
/**
* 过滤敏感词
* @param text 待过滤的文本
* @return 过滤后的文本
*/
public String filter(String text){
if(StringUtils.isBlank(text)){
return null;
}
//指针1 ,指向树
TrieNode tempNode = rootNode;
//指针2 指向字符串的慢指针,一直往前走
int begin = 0;
//指针3 指向字符串的快指针,往后走检查,如果不是就归位。
int position = 0;
//结果
StringBuilder result = new StringBuilder();
while(position < text.length()){
char c = text.charAt(position);
//跳过符号
if(isSymbol(c)){
//若指针处于根节点。则将此符号计入结果,让指针向下走一步。
if(tempNode == rootNode){
result.append(c);
begin++;
}
//无论结构在开头或中间指针3都向下走一步。
position++;
continue;
}
tempNode = tempNode.getSubNode(c);
if(tempNode == null){
// 以begin为开头的字符串不存在敏感词
result.append(text.charAt(begin));
//进入下一个位置
position = ++begin;
// 重新指向根节点。
tempNode = rootNode;
}else if(tempNode.isKeyWordEnd()){
//发现了敏感词,将begin-posttion中的字符串替换
result.append(REPLACEMENT);
// 进入下一个位置。
begin = ++ position;
tempNode = rootNode;
}else{
//检查下一个字符
position++;
}
}
//将最后一批字符计入结构
result.append(text.substring(begin));
return result.toString();
}
//判断是否为符号,是的话返回true,不是的话返回false
public boolean isSymbol(Character c){
//!CharUtils.isAsciiAlphanumeric(c)判断合法字符
//c < 0x2E80 || c > 0x9fff 东亚文字的范围是0x2E80到0x9fff
return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9fff);
}
//定义一个内部类,作为前缀树的结构
private static class TrieNode{
//关键词结束的标识
private boolean isKeyWordEnd = false;
//子节点(key 代表下级的节点字符, value是下级节点)
private Map<Character, TrieNode> subNode = new HashMap<>();
public boolean isKeyWordEnd() {
return isKeyWordEnd;
}
public void setKeyWordEnd(boolean keyWordEnd) {
isKeyWordEnd = keyWordEnd;
}
//添加子节点
public void addSubNode(Character key,TrieNode subNode){
this.subNode.put(key,subNode);
}
//获取子节点
public TrieNode getSubNode(Character key){
return subNode.get(key);
}
}
}
测试类
SensitiveTest
package com.nacl;
import com.nacl.util.SensitiveUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author HAOYANG
* @create 2023-07-31 15:09
*/
@SpringBootTest
public class SensitiveTest {
@Autowired
private SensitiveUtil sensitiveFilter;
@Test
public void test(){
String test = "色情电影";
String result = sensitiveFilter.filter(test);
System.out.println(result);
}
}
效果图
总结
最终的屏蔽词汇效果还是要看sensitive-words.txt文件中写的要屏蔽的词汇