敏感词过滤分为三个步骤,1.定义前缀树,2.初始化前缀数,3.编写过滤敏感词的方法
如下图:
在resource下编辑一个txt文件,提前写好自己要过滤的词汇,一定要看看target-classes下有没有这个txt文件,没有的话可以在maven中compile一下,下图都已经标注好了。(敏感词我划了,审核不通过,祖安的化语瞬间涌上心头)
废话不多说直接上代码(本项目用springboot骨架搭建)
@Component
public class SensitiveFilter {
//初始化前缀树
//实例化一个logger 有的需要日志去记录一下
private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);
//定义一个常量表明替换的内容
private static final String REPLACEMENT = "***";
//根节点
private TrieNode rootNode =new TrieNode();
@PostConstruct//表示这是一个初始化方法 在服务启动的时候就初始化了,这个方法就被调用了
public void init(){
//加载的是一个字节流
try (
InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
//将字节流转换成字符流,得到一个缓冲流
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
){
//读取每一个敏感词
String keyword;
while ((keyword = reader.readLine())!=null){
//添加到前缀树
this.addKeyWord(keyword);
}
} catch (IOException e) {
logger.error("加载敏感词文件失败"+e.getMessage());
}
}
//添加到前缀树,将敏感词添加到前缀树去
private void addKeyWord (String keyword){
TrieNode tempNode = rootNode;
for (int i = 0; i <keyword.length() ; i++) {
char c = keyword.charAt(i);
TrieNode subNode = tempNode.getSubNode(c);
if (subNode == null){
//初始化子节点
subNode = new TrieNode();
tempNode.addSubNode(c,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 sb = new StringBuilder();
while (position <text.length()){
char c = text.charAt(position);
//跳过符号
if (isSymbol(c)){
//若指针1处于根节点,将此符号计入结果让指针2向下走一步
if (tempNode == rootNode){
sb.append(c);
begin++;
}
//无论符号在开头或中间,指针三都向下走一步
position++;
continue;
}
//检查下级节点
tempNode = tempNode.getSubNode(c);
if (tempNode == null){
//以begin开头的字符串不是敏感词
sb.append(text.charAt(begin));
//进入下一个位置
position = ++begin;
//重新指向根节点
tempNode = rootNode;
}else if(tempNode.isKeyWordEnd()){
//发现了敏感词 begin-position 替换成***
sb.append(REPLACEMENT);
//进入下一个位置
begin = ++ position;
//重新指向根节点
tempNode = rootNode;
}else {
//检查下一个字符
position++;
}
}
//将最后一批字符计入结果
sb.append(text.substring(begin));
return sb.toString();
}
//判断跳过符号的方法编写
private boolean isSymbol(Character c){
//判断字符是不是普通的字符,特殊字符返回false
// 0x9FFF - 0x2E80是东亚的文字范围
return !CharUtils.isAsciiAlphanumeric(c) && (c<0x2E80||c>0x9FFF);
}
//定义前缀树
//定义一个内部类,TrieNode前缀树,描述数据的某个节点
private class TrieNode {
//关键词结束标识
private boolean isKeyWordEnd = false;
//子节点 Character(下级字符)子节点对应的那个字符 TrieNode(下级节点)
private Map<Character, TrieNode> subNodes = new HashMap<>();
public boolean isKeyWordEnd() {
return isKeyWordEnd;
}
public void setKeyWordEnd(boolean keyWordEnd) {
isKeyWordEnd = keyWordEnd;
}
//添加子节点
public void addSubNode(Character c, TrieNode node) {
subNodes.put(c, node);
}
//获取子节点
public TrieNode getSubNode(Character c) {
return subNodes.get(c);
}
}
}
编写测试类代码
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = Hb5806Application.class)
public class SensitiveTests {
@Autowired
private SensitiveFilter sensitiveFilter;
@Test
public void testSensitiveFilter() {
String text = "自己设置敏感词";
String filter = sensitiveFilter.filter(text);
System.out.println(filter);
}
}
测试结果:
敏感词都替换成了***