自敏感词过滤-使用工具类来实现--有穷自动机-DFA算法

文章目录


提示:以下是本篇文章正文内容,下面案例可供参考

一、自管理敏感词库实现和原理

比如媒体公司,除了使用第三方的敏感词审核,每个平台也会有自己的敏感词库。

1.实现流程:

 

二、使用步骤

1.算法实现:引入工具类

代码如下(示例):拿走直接用。算法实现没有依赖。

package com.ciels.utils.common;


import java.util.*;

public class SensitiveWordUtil {

    public static Map<String, Object> dictionaryMap = new HashMap<>();


    /**
     * 生成关键词字典库
     * @param words
     * @return
     */
    public static void initMap(Collection<String> words) {
        if (words == null) {
            System.out.println("敏感词列表不能为空");
            return ;
        }

        // map初始长度words.size(),整个字典库的入口字数(小于words.size(),因为不同的词可能会有相同的首字)
        Map<String, Object> map = new HashMap<>(words.size());
        // 遍历过程中当前层次的数据
        Map<String, Object> curMap = null;
        Iterator<String> iterator = words.iterator();

        while (iterator.hasNext()) {
            String word = iterator.next();
            curMap = map;
            int len = word.length();
            for (int i =0; i < len; i++) {
                // 遍历每个词的字
                String key = String.valueOf(word.charAt(i));
                // 当前字在当前层是否存在, 不存在则新建, 当前层数据指向下一个节点, 继续判断是否存在数据
                Map<String, Object> wordMap = (Map<String, Object>) curMap.get(key);
                if (wordMap == null) {
                    // 每个节点存在两个数据: 下一个节点和isEnd(是否结束标志)
                    wordMap = new HashMap<>(2);
                    wordMap.put("isEnd", "0");
                    curMap.put(key, wordMap);
                }
                curMap = wordMap;
                // 如果当前字是词的最后一个字,则将isEnd标志置1
                if (i == len -1) {
                    curMap.put("isEnd", "1");
                }
            }
        }

        dictionaryMap = map;
    }

    /**
     * 搜索文本中某个文字是否匹配关键词
     * @param text
     * @param beginIndex
     * @return
     */
    private static int checkWord(String text, int beginIndex) {
        if (dictionaryMap == null) {
            throw new RuntimeException("字典不能为空");
        }
        boolean isEnd = false;
        int wordLength = 0;
        Map<String, Object> curMap = dictionaryMap;
        int len = text.length();
        // 从文本的第beginIndex开始匹配
        for (int i = beginIndex; i < len; i++) {
            String key = String.valueOf(text.charAt(i));
            // 获取当前key的下一个节点
            curMap = (Map<String, Object>) curMap.get(key);
            if (curMap == null) {
                break;
            } else {
                wordLength ++;
                if ("1".equals(curMap.get("isEnd"))) {
                    isEnd = true;
                }
            }
        }
        if (!isEnd) {
            wordLength = 0;
        }
        return wordLength;
    }

    /**
     * 获取匹配的关键词和命中次数
     * @param text
     * @return
     */
    public static Map<String, Integer> matchWords(String text) {
        Map<String, Integer> wordMap = new HashMap<>();
        int len = text.length();
        for (int i = 0; i < len; i++) {
            int wordLength = checkWord(text, i);
            if (wordLength > 0) {
                String word = text.substring(i, i + wordLength);
                // 添加关键词匹配次数
                if (wordMap.containsKey(word)) {
                    wordMap.put(word, wordMap.get(word) + 1);
                } else {
                    wordMap.put(word, 1);
                }

                i += wordLength - 1;
            }
        }
        return wordMap;
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("法轮");
        list.add("法轮功");
        list.add("冰毒");
        initMap(list);
        String content="我是一个好人,并不会卖冰毒,也不操练法轮功,我真的不卖冰毒";
        Map<String, Integer> map = matchWords(content);
        System.out.println(map);
    }
}

2.如何使用工具类

因为敏感词使用的是非常频繁的,所以我们把自管理的敏感词加入到Redis中。提高效率,同时在修改数据库中的自管理敏感词的时候,需要同时去更新Redis缓存中的数据。(这些操作没做)

代码如下(示例):

String text 传入要审核的文本
WmNews wmNews 传入要审核的文章对象。
后续需要更新其状态-->status文章发布状态、reason 审核不通过原因

private boolean scanSensitive(String text, WmNews wmNews) {
        //得到所有子管理敏感词的列表,从数据库里查询性能低。使用redis的set结构
        //.members 得到所有数据
        Set<String> sensitives = redisTemplate.opsForSet().members("wmnews:sensitive");
        if (CollectionUtils.isEmpty(sensitives)){
            //没有缓存从数据库查询
            List<WmSensitive> wmSensitivesList = wmSensitiveMapper.selectList(null);
            if (CollectionUtils.isEmpty(wmSensitivesList)){
                //没有敏感词 审核通过
                return true;
            }
            //把wmSensitivesList转换成  敏感词汇的列表
            sensitives=wmSensitivesList.stream().map(WmSensitive::getSensitives).collect(Collectors.toSet());
            //添加到redis缓存里:因为添加到set需要数组类型的参数,所以把敏感词列表转换成数组,再添加到redis里
            String[] array = sensitives.toArray(new String[sensitives.size()]);
            redisTemplate.opsForSet().add("wmnews:sensitive",array);
        }
        //在使用自管理敏感词进行审核:检查一下文本里面是否包含了任意一个敏感词。
        //使用DFA有穷自动机实现。不用一个词一个词的去str.contains()来判断,提高了效率   -----使用工具类来实现。
        if (CollectionUtils.isEmpty(SensitiveWordUtil.dictionaryMap)){
            SensitiveWordUtil.initMap(sensitives);//优化不用每次都初始化这个集合。但是敏感词修改  后需要重新加载集合。
        }
        Map<String, Integer> resultMap = SensitiveWordUtil.matchWords(text);
        if (resultMap.size()>0){
            //如果有敏感词,就不通过。需要更新WnNews的状态和原因
            wmNews.setStatus(WmNews.Status.FAIL.getCode());
            wmNews.setReason("文本中有敏感词:"+resultMap.keySet());
            wmNewsMapper.updateById(wmNews);
            return false;
        }
        return true;
    }

 三、使用的表如示例:

四、一些API总结

        1.CollectionUtils.isEmpty(集合);//判断集合是否为空
        2.SetOperations<String, String> set = redisTemplate.opsForSet();//得到操作redis set                    
          类型的对象
         Set<String> setMembers = set.members("wmnews:sensitive");//得到以key的set集合所有值
        String[] strings = sensitives.toArray(new String[sensitives.size()]);
        set.add("wmnews:sensitive",strings);//存储set的时候可以传入一个数组。
            add(String key,String...value)可变参数可以传入数组使用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值