首頁  >  文章  >  Java  >  SpringBoot如何實現過濾敏感詞

SpringBoot如何實現過濾敏感詞

王林
王林轉載
2023-05-20 19:28:042095瀏覽

過濾敏感字

SpringBoot如何實現過濾敏感詞

#1. 建立一個儲存要過濾的敏感字的文字檔

#先建立一個文字檔案儲存要過濾的敏感字

SpringBoot如何實現過濾敏感詞

在下面的工具類別中我們會讀取這個文字文件,這裡提前給出

@PostConstruct   // 这个注解表示当容器实例化这个bean(服务启动的时候)之后在调用构造器之后这个方法会自动的调用
public void init(){
    try(
            // 读取写有“敏感词”的文件,getClass表示从程序编译之后的target/classes读配置文件,读之后是字节流
            // java7语法,在这里的句子最后会自动执行close语句
            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());
    }
}

2. 發展過濾敏感詞的工具類

開發過濾敏感詞組件

為了方便以後復用,我們把過濾敏感詞寫成一個工具類SensitiveFilter。

@Component
public class SensitiveFilter {

    private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);

    // 当检测到敏感词后我们要把敏感词替换成什么符号
    private static final String REPLACEMENT = "***";

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

    @PostConstruct   // 这个注解表示当容器实例化这个bean(服务启动的时候)之后在调用构造器之后这个方法会自动的调用
    public void init(){
        try(
                // 读取写有“敏感词”的文件,getClass表示从程序编译之后的target/classes读配置文件,读之后是字节流
                // java7语法,在这里的句子最后会自动执行close语句
                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为空,初始化子节点;subNode不为空,直接用就可以了
                subNode = new TrieNode();
                tempNode.addSubNode(c, subNode);
            }
            // 指针指向子节点,进入下一轮循环
            tempNode = subNode;
        }
        // 最后要设置结束标识
        tempNode.setKeywordEnd(true);
    }

    /**
     * 过滤敏感词
     * @param text 待过滤的文本
     * @return  过滤后的文本
     */
    public String filter(String text){
        if(StringUtils.isBlank(text)){
            // 待过滤的文本为空,直接返回null
            return null;
        }
        // 指针1,指向树
        TrieNode tempNode = rootNode;
        // 指针2,指向正在检测的字符串段的首
        int begin = 0;
        // 指针3,指向正在检测的字符串段的尾
        int position = 0;
        // 储存过滤后的文本
        StringBuilder sb = new StringBuilder();
        while (begin < text.length()){
            char c = text.charAt(position);

            // 跳过符号,比如 “开票”是敏感词 #开#票# 这个字符串中间的 &#39;#&#39; 应该跳过
            if(isSymbol(c)){
                // 是特殊字符
                // 若指针1处于根节点,将此符号计入结果,指针2、3向右走一步
                if(tempNode == rootNode){
                    sb.append(c);
                    begin++;
                }
                // 无论符号在开头或中间,指针3都向下走一步
                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字符串替换掉,存 REPLACEMENT (里面是***)
                sb.append(REPLACEMENT);
                // 进入下一个位置
                begin = ++position;
                // 重新指向根节点
                tempNode = rootNode;
            } else {
                // 检查下一个字符
                position++;
            }
        }
        return sb.toString();
    }

    // 判断是否为特殊符号,是则返回true,不是则返回false
    private boolean isSymbol(Character c){
        // CharUtils.isAsciiAlphanumeric(c)方法:a、b、1、2···返回true,特殊字符返回false
        // 0x2E80  ~  0x9FFF 是东亚的文字范围,东亚文字范围我们不认为是符号
        return  !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
    }

    // 前缀树
    private class TrieNode{

        // 关键词结束标识
        private boolean isKeywordEnd = false;

        // 当前节点的子节点(key是下级字符、value是下级节点)
        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);
        }
    }
}

SpringBoot如何實現過濾敏感詞

上面就是過濾敏感詞工具類別的全部程式碼,接下來我們來解釋一下開發步驟

開發過濾敏感詞元件分為三步:

1.定義前綴樹(Tree)

我們將定義前綴樹寫為SensitiveFilter工具類別的內部類別

// 前缀树
private class TrieNode{

    // 关键词结束标识
    private boolean isKeywordEnd = false;

    // 当前节点的子节点(key是下级字符、value是下级节点)
    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);
    }
}

SpringBoot如何實現過濾敏感詞

2.根據敏感詞,初始化前綴樹

將敏感詞加入前綴樹

// 将一个敏感词添加到前缀树中
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为空,初始化子节点;subNode不为空,直接用就可以了
            subNode = new TrieNode();
            tempNode.addSubNode(c, subNode);
        }
        // 指针指向子节点,进入下一轮循环
        tempNode = subNode;
    }
    // 最后要设置结束标识
    tempNode.setKeywordEnd(true);
}

SpringBoot如何實現過濾敏感詞

3.寫出過濾敏感字的方法

如何篩選文字中的敏感字:

SpringBoot如何實現過濾敏感詞

特殊符號怎麼處理:

SpringBoot如何實現過濾敏感詞

敏感詞前綴樹初始化完畢之後,過濾文字中的敏感詞的演算法應該如下:

定義三個指標:

  • 指標1指向Tree樹

  • #指標2指向待過濾字串段

  • 指標3指向待過濾字串段

SpringBoot如何實現過濾敏感詞

/**
 * 过滤敏感词
 * @param text 待过滤的文本
 * @return  过滤后的文本
 */
public String filter(String text){
    if(StringUtils.isBlank(text)){
        // 待过滤的文本为空,直接返回null
        return null;
    }
    // 指针1,指向树
    TrieNode tempNode = rootNode;
    // 指针2,指向正在检测的字符串段的首
    int begin = 0;
    // 指针3,指向正在检测的字符串段的尾
    int position = 0;
    // 储存过滤后的文本
    StringBuilder sb = new StringBuilder();
    while (begin < text.length()){
        char c = text.charAt(position);

        // 跳过符号,比如 “开票”是敏感词 #开#票# 这个字符串中间的 &#39;#&#39; 应该跳过
        if(isSymbol(c)){
            // 是特殊字符
            // 若指针1处于根节点,将此符号计入结果,指针2、3向右走一步
            if(tempNode == rootNode){
                sb.append(c);
                begin++;
            }
            // 无论符号在开头或中间,指针3都向下走一步
            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字符串替换掉,存 REPLACEMENT (里面是***)
            sb.append(REPLACEMENT);
            // 进入下一个位置
            begin = ++position;
            // 重新指向根节点
            tempNode = rootNode;
        } else {
            // 检查下一个字符
            position++;
        }
    }
    return sb.toString();
}

// 判断是否为特殊符号,是则返回true,不是则返回false
private boolean isSymbol(Character c){
    // CharUtils.isAsciiAlphanumeric(c)方法:a、b、1、2···返回true,特殊字符返回false
    // 0x2E80  ~  0x9FFF 是东亚的文字范围,东亚文字范围我们不认为是符号
    return  !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
}

最後:建議在測試類別中測試一下SpringBoot如何實現過濾敏感詞

經過測試,過濾敏感字的工具類開發完成,這個工具會在接下來的

發佈貼文###的功能中用到。 ###

以上是SpringBoot如何實現過濾敏感詞的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除