springboot 实现自动屏蔽敏感词过滤器

本文介绍了一种基于前缀树的敏感词过滤方法,通过构建前缀树存储敏感词汇,并利用双指针技术进行文本扫描,有效过滤敏感词。文章详细讲解了前缀树节点设计、敏感词添加过程及文本过滤算法。

1.编辑敏感词文本(.txt)

在这里插入图片描述

按行写入

2.编写过滤器

(1)创建前缀树保存敏感词

//前缀树
private class TireNode {
    //关键词结束标志
    private boolean isKeywordsEnd = false;
    //key是下级字符,value是下级节点(root为null)
    private Map<Character, TireNode> sonNode = new HashMap<>();

    public boolean isKeywordsEnd() {
        return isKeywordsEnd;
    }

    public void setKeywordsEnd(boolean keywordsEnd) {
        isKeywordsEnd = keywordsEnd;
    }

    public void addSonNode(Character val, TireNode son) {
        sonNode.put(val, son);
    }

    public TireNode getSonNode(Character val) {
        return sonNode.get(val);
    }
}

在这里插入图片描述
标志代表是否是word结束,判断时若扫描结束节点非结束标志,则不视为敏感词。

(2)将文本导入

@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.addKeywords(keyword);
        }
    } catch (Exception e) {
        logger.error("服务器加载敏感词失败" + e.getMessage());
    }
}

(3)将敏感词添加到前缀树

//将敏感词添加到前缀树
private void addKeywords(String keyword) {
    //指向root
    TireNode cur = tireNode;
    for (int i = 0; i < keyword.length(); i++) {
        //子节点中没有该字符
        if (cur.getSonNode(keyword.charAt(i)) == null) {
            //新建节点插入
            cur.addSonNode(keyword.charAt(i), new TireNode());
        }
        //指向下一个节点
        cur = cur.getSonNode(keyword.charAt(i));
        //设置标志位
        if (i == keyword.length() - 1) cur.setKeywordsEnd(true);
    }
}

(4)过滤敏感词

/**
* 过滤敏感词
*
* @param text 待过滤的敏感词
* @return *****
*/
public String filter(String text) {
   if (text == null) return "";
   int main = 0, ass = 0;
   TireNode cur = tireNode;
   StringBuilder builder = new StringBuilder();
   while (ass < text.length()) {
       char c = text.charAt(ass);
       if (isSymbol(c)) {
           //如果主指针处于根节点,将此符号计入结果,让副指针继续走
           if (cur == tireNode) {
               builder.append(c);
               main++;
           }
           //副指针只要碰到符号就后移,主指针只有未开始匹配(树指针为根节点)时碰到符号才后移
           ass++;
           continue;
       }
       //寻找当前副指针指向的字符是否在树里
       cur = cur.getSonNode(c);
       //如果不在将主指针指向的字符压入builder,主指针向后移,树指针指向根节点
       if (cur == null) {
           builder.append(text.charAt(main));
           ass = ++main;
           cur = tireNode;
       } else if (cur.isKeywordsEnd()) {
           //如果副指针指向标志结束处,将代替符号压入builder,主副指针移至副指针后一个
           builder.append(REPLACEMENT);
           main = ++ass;
           cur = tireNode;
       } else {
           //否则副指针后移继续检查
           ass++;
       }
   }
   //将最后一批字符压入
   builder.append(text.substring(main));
   return builder.toString();
}

//判断是否为特殊符号
private boolean isSymbol(Character c) {
   //0x2E80~0x9FFF是东亚文字
   //CharUtils.isAsciiAlphanumeric判断是否为特殊符号
   return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
}

在这里插入图片描述

  • 当走到a时,树指针向下移动到a,副指针继续向后走
    在这里插入图片描述
  • 此时发现结束标记,将abc变为***,树指针指向root

ps:要注意跳过特殊符号,防止敏感词中间加符号无法辨别,因此要检测是否为符号

3.测试

在这里插入图片描述
点击看gitee源码!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值