使用前缀树过滤敏感词

定义前缀树类

public class TrieNode {
    // 是否为关键词结尾
    private boolean end = false;

    // 当前节点的所有的子节点
    private Map<Character, TrieNode> subNodes = new HashMap<Character, TrieNode>();

    // 添加子节点
    public void addSubNode(Character key, TrieNode node) {
        this.subNodes.put(key, node);
    }

    // 查询子节点
    public TrieNode getSubNode(Character key) {
        return this.subNodes.get(key);
    }

    public boolean isEnd() {
        return end;
    }

    public void setEnd(boolean end) {
        this.end = end;
    }
}

敏感词过滤服务

@Service
public class SensitiveService implements InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(SensitiveService.class);

    // 字典树根节点
    private TrieNode rootNode = new TrieNode();

    /**
     * Spring在构建Bean时自动调用该方法
     * 读取敏感词文件,并构建字典树
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        try {
            // 从资源文件中读入
            InputStream in = Thread.currentThread().getContextClassLoader().
                    getResourceAsStream("sensitiveWords.txt");
            InputStreamReader isr = new InputStreamReader(in);
            BufferedReader br = new BufferedReader(isr);

            // 每行一个敏感词
            String line = null;
            while((line = br.readLine()) != null) {
                addWord(br.readLine() );
            }

            isr.close();
        } catch (Exception e) {
            logger.error("读取敏感词文件失败" + e.getMessage());
        }
    }

    /**
     * 是否为非数字、大小写字母、汉字 字符
     * @param ch
     * @return
     */
    private boolean isSymbol(char ch) {
        return Pattern.matches("[^0-9a-zA-Z\u4e00-\u9fa5]", ""+ch);
    }

    /**
     * 增加关键词
     * @param lineText
     */
    public void addWord(String lineText) {
        TrieNode tmpNode = rootNode;
        for(int i=0; i<lineText.length(); i++) {
            Character ch = lineText.charAt(i);
            if(isSymbol(ch))
                continue;
            TrieNode node = tmpNode.getSubNode(ch);

            if(node == null) {
                node = new TrieNode();
                tmpNode.addSubNode(ch, node);
            }

            tmpNode = node;

            if(i == lineText.length() - 1) {
                tmpNode.setEnd(true);
            }
        }
    }

    /**
     * 过滤敏感词
     * @param text
     * @return
     */
    public String filter(String text) {
        if(StringUtils.isEmpty(text)) {
            return text;
        }
        // 存储过滤后的字符串
        StringBuilder result = new StringBuilder();

        // 敏感词替换文本
        String replaceStr = "***";
        // 字典树上的比较指针
        TrieNode tmpNode = rootNode;
        // 词首指针
        int begin = 0;
        // 位置指针
        int position = 0;

        while(position < text.length()) {
            // 获取带比较字符
            char ch = text.charAt(position);

            // 这里的比较是为了忽略敏感词间的特殊字符,比如 色-_-情
            if(isSymbol(ch)) {
                // 如果是开头,那就直接写入结果字符串
                if(tmpNode == rootNode) {
                    result.append(ch);
                    begin++;
                }
                position++;
                continue;
            }

            // 读取子节点该字符对应的子节点
            tmpNode = tmpNode.getSubNode(ch);

            if(tmpNode == null) { // 不存在,换下个字符开头
                result.append(text.charAt(begin));
                position = begin + 1;
                begin = position;
                tmpNode = rootNode;
            } else if(tmpNode.isEnd()) { // 刚好是敏感词,替换,以敏感词后的第一个字符开头
                // 发现敏感词
                result.append(replaceStr);
                position = position + 1;
                begin = position;
                tmpNode = rootNode;
            } else { // 有这个词,但不是敏感词,继续看看后面的词
                position++;
            }
        }
        result.append(text.substring(begin));

        return result.toString();
    }
}

猜你喜欢

转载自blog.csdn.net/goldlone/article/details/81563352