敏感词过滤

最近花钱买了一个牛客网上的项目,学习一下业务上的逻辑。这个项目是用SpringBoot开发的,省去了一系列的配置,十分方便。现在这个项目已经做到了站内信的地方,之后把整个项目搬到博客上,当做纪念。

现在把其中的一部分先搬上来,这部分内容是敏感词过滤。这部分算法涉及到字典树和字符匹配的方法,值的研究一下。整体步骤如下所示:

首先创建字典树

其次读取敏感词的属性文件

接着将敏感词组成字典树

最后把字符串与字典树进行匹配,即匹配算法

代码如下所示,具体解析已经全部放在代码里了。

package com.xuan.xupro.Service;

import com.xuan.xupro.Controller.IndexController;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;


import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

/**
 *敏感词过滤服务
 * Created by Administrator on 2018/2/21.
 */
public class SensitiveService implements InitializingBean {
    //添加日志文件
    private static final Logger logger = LoggerFactory.getLogger(SensitiveService.class);
    //敏感词替换为“**”
    private String DEFAULT_WORD = "**";
    //字典树,所有敏感词均在字典树上
    class TireNode{
        //每个树结点对应的是一个map,以字符映射树结点
        private Map<Character, TireNode> subNode = new HashMap<Character, TireNode>();
        //敏感词的颜色判断,若为结尾,则该标识为true
        private boolean endcolor;
        //得到该树节点的下一个树结点
        TireNode getSubNode(Character c) {
            return subNode.get(c);
        }
        //为该树节点增加下一个树节点
        void addSubNode(Character c, TireNode tireNode) {
            subNode.put(c,tireNode);
        }
        //判断当前树节点是否为根节点
        boolean isEndcolor() {
            return endcolor;
        }
        //给根节点设置标识
        void setEndcolor(boolean endcolor) {
            this.endcolor = endcolor;
        }
    }

    private TireNode rootNode = new TireNode();
    //判断该树节点的字符是否为标点符号
    private boolean isSympol(Character c){
        //char转化为int,其中c拆箱,从Character自动转化为char
        int inc = (int)c;
        //若为标点符号,则返回ture
        return !CharUtils.isAsciiAlphanumeric(c) && (inc < 0x2E80 || inc > 0x9FFF);
    }
    //该方法为向字典树里添加敏感词
    private void addWord(String string){
        //rootNode的引用赋予当前树节点tempNode,此后便是对tempNode结点的操作
        TireNode tempNode = rootNode;

        for(int i = 0; i < string.length(); i++){
            //将该索引下的字符转化为char,再自动装箱为Character
            Character c = string.charAt(i);
            //如果该索引下的字符为标点符号,则跳过此次循环,执行下一个循环
            if(!isSympol(c)){
                continue;
            }
            //判断tempNode下,是否有字符c,之后及相应的子节点node
            TireNode node = tempNode.getSubNode(c);
            //如果node不存在,即该字典树没有字符ctempNode结点添加字符c
            if(node == null){
                node = new TireNode();
                tempNode.addSubNode(c, node);
            }
            //如果有字符c,则把node的引用赋予tempNode
            tempNode = node;
            //如果该循环的索引为String的最后一位,则将树节点tempNode标志为true
            if(i == string.length() - 1){

                tempNode.setEndcolor(true);

            }
        }

    }

    private String sensitiveWordMatch(String str){
        //先判断字符串是否为空
        if(StringUtils.isBlank(str)){
            return "";
        }
        /*sb为替换掉敏感词的String,在一个方法里使用sb,必定为
          线程安全,因此使用StringBuilder
        */
        StringBuilder sb = new StringBuilder();
        /*接下来开始匹配敏感词
          pos为一个匹配过程中匹配的位置
          cur为当前开始匹配过程时的匹配位置
          tempNode为字典树的根节点
         */
        int pos = 0;
        int cur = 0;
        TireNode tempNode = rootNode;
        //判断cur是否走到str尽头
        while(cur < str.length()) {
            //判断一个匹配过程中是否存在敏感词
            while (pos < str.length()) {
                //当前匹配位置的字符
                Character c = str.charAt(pos);
                //判断该字符是否为标点符号
                if(isSympol(c)){
                    sb.append(c);
                    //如果该字符为匹配的第一个字符,则cur++
                    if(tempNode == rootNode){
                        cur++;
                    }
                    //如果不是第一个,则把pos++即可
                    pos++;
                    continue;
                }
                //根据字符c得到当前节点
                tempNode = tempNode.getSubNode(c);
                //如果tempNode不为空
                if (tempNode != null) {
                    /*再次判断,该结点是否为最后一位,若是,则将匹配到的
                    的词替换为***,同时,令pospos+1cur=pos,返回根
                    结点
                     */
                    if (tempNode.isEndcolor()) {
                        sb.append(DEFAULT_WORD);
                        pos = pos + 1;
                        cur = pos;
                        tempNode = rootNode;
                    //否则只将pos++
                    } else {
                        pos++;
                    }
                //tempNode为空,说明cur位置处匹配不到敏感词。
                } else {
                    sb.append(c);
                    cur = cur + 1;
                    pos = cur;

                }
            }
            /*若在一次匹配过程中pos越界了,则说明cur位置处匹配不到
            敏感词,但不代表cur位置以后的地方匹配不到敏感词。因此,
            需要判断cur是否越界,如果不越界,则需再次进行敏感词匹配。
             */
            //判断cur是否越界,若不越界,则把当前cur索引下的字符加入。
            if(cur < str.length() - 1){
                sb.append(str.indexOf(cur));
                cur++;
                pos = cur;
                tempNode = rootNode;
            }
        }
        return sb.toString();
    }
    //该方法为重写InitializingBean的方法,在bean初始化时,执行该方法
    @Override
    public void afterPropertiesSet() throws Exception{
        //首先从文件中读取敏感词集合
       try {
           InputStream is = new FileInputStream("SensitiveWord");
           InputStreamReader reader = new InputStreamReader(is);
           BufferedReader br = new BufferedReader(reader);
           String str;
           //每读一行便将其加入到字典树中,直到将敏感词加入到字典树中完毕
           while (br.readLine() != null) {
               str = br.readLine().trim();
               addWord(str);
           }
           //关闭资源,否则会一直阻塞住。
           reader.close();
       }catch (Exception e){
           //log文件读取错误信息
           logger.error("读取敏感词失败" + e.getMessage());
       }

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值