小工具      在线工具  汉语词典  css  js  c++  java

必须做,过滤敏感词的最佳实践

java 额外说明

收录于:40天前

因为博客中评论功能是在线的,但是为了防止某些人发布一些敏感词,所以做这个敏感词过滤是非常有必要的(这也是我不想在评论中添加图片的原因。如果有人发布一些如何组织奇怪的图片-)

博客敏感词政策是检测文案中是否存在敏感词,将不允许发布,并对相应的敏感词进行整改,而不是在评论中打上*号,以防止评论被删除。充满********,哈哈哈

一、 DFA 算法

DFA,全称Deterministic Finite Automaton,即确定性有限自动机:通过一系列事件从一种状态转换到另一种状态,即状态->事件->状态

大概是这么个意思,通过节点获取到下一个节点是否符合敏感词,这样只要循环一遍文案就可以找到所有的敏感词了,以上图片的敏感词分别为:敏感词敏捷坏人坏蛋

二、sensitive-word 工具包

当然有需求就有实现,有现成的工具类sensitive-word,具体使用方式可参考:https://blog.csdn.net/yumuing/article/details/129717450
我就不重复写了,博客里使用的就是这个工具包

三、自定义实现

当然,不想重新发明轮子的程序员不是好的架构师。现在我们已经了解了这个算法的基本原理,下面我们来手动实现这个功能。

1. 创建节点

这个可怜的状态机与链表非常相似,所以我们首先创建一个链表节点,创建子节点和结束状态

public class TrieNode implements Serializable {
    

    /** * 子节点 */
    private Map<Character, TrieNode> children = new HashMap<>();

    /** * 是否为最后一个字符 */
    private Boolean isEndOfWord = false;

    public Map<Character, TrieNode> getChildren() {
    
        return children;
    }

    public Boolean getEndOfWord() {
    
        return isEndOfWord;
    }

    public void setEndOfWord(Boolean endOfWord) {
    
        isEndOfWord = endOfWord;
    }
}

2. 初始化数据

从准备好的敏感词txt文件中读取敏感词,并放入到List集合中,注意这个txt中一行为一个敏感词,不要有逗号,空格等等字符
敏感词库网上很多,这里推荐一个:https://github.com/fwwdn/sensitive-stop-words
在resource包下,创建sensitive目录,并加入mySensitiveWords.txt敏感词库

private List<String> sensitiveList;

private void loadData() {
    
    try {
    
        Resource mySensitiveWords = new ClassPathResource("sensitive/mySensitiveWords.txt");
        Path mySensitiveWordsPath = Paths.get(mySensitiveWords.getFile().getPath());
        sensitiveList = Files.readAllLines(mySensitiveWordsPath, StandardCharsets.UTF_8);
    } catch (Exception e) {
    
        log.error("敏感词初始化失败", e);
    }
}

/** * 初始化数据 */
private void init() {
    
    for (String word : sensitiveList) {
    
        addSensitive(word);
    }
}

/** * 添加敏感词 * * @param word */
public void addSensitive(String word) {
    
    TrieNode node = root;
    for (char ch : word.toCharArray()) {
    
        // computeIfAbsent返回结果值
        node = node.getChildren().computeIfAbsent(ch, k -> new TrieNode());
    }
    // 一个敏感词循环结束,设置结束标记
    node.setEndOfWord(true);
}

3. 工具类编写

这里我把所有的工具方法都贴出来了,大家可以直接使用


@Slf4j
public enum SensitiveUtil {
    

    /** * 工具类 */
    X;

    /** * 根节点 */
    private TrieNode root = new TrieNode();


    /** * 敏感词列表 */
    private List<String> sensitiveList;

    SensitiveUtil() {
    
        // 加载数据
        loadData();
        // 初始化数据
        init();
    }

    /** * 加载数据 */
    private void loadData() {
    
        try {
    
            Resource mySensitiveWords = new ClassPathResource("sensitive/mySensitiveWords.txt");
            Path mySensitiveWordsPath = Paths.get(mySensitiveWords.getFile().getPath());
            sensitiveList = Files.readAllLines(mySensitiveWordsPath, StandardCharsets.UTF_8);
        } catch (Exception e) {
    
            log.error("敏感词初始化失败", e);
        }
    }

    /** * 初始化数据 */
    private void init() {
    
        for (String word : sensitiveList) {
    
            addSensitive(word);
        }
    }

    /** * 添加敏感词 * * @param word */
    public void addSensitive(String word) {
    
        TrieNode node = root;
        for (char ch : word.toCharArray()) {
    
            // computeIfAbsent返回结果值
            node = node.getChildren().computeIfAbsent(ch, k -> new TrieNode());
        }
        // 一个敏感词循环结束,设置结束标记
        node.setEndOfWord(true);
    }

    /** * 判断是否存在敏感词 */
    public boolean contains(String content) {
    
        TrieNode currentNode = root;
        for (char ch : content.toCharArray()) {
    
            TrieNode node = currentNode.getChildren().get(ch);
            if (node != null) {
    
                if (node.getEndOfWord()) {
    
                    return true;
                }
                currentNode = node;
            } else {
    
                currentNode = root;
            }
        }
        return false;
    }

    /** * 返回第一个敏感词 */
    public String findFirst(String text) {
    
        Set<String> first = find(text, true);
        if (!first.isEmpty()) {
    
            return first.iterator().next();
        }
        return null;
    }

    /** * 返回所有敏感词 */
    public Set<String> findAll(String text) {
    
        return find(text, false);
    }

    /** * 返回敏感词 */
    public Set<String> find(String text, boolean isFirst) {
    
        TrieNode currentNode = root;
        Set<String> set = new HashSet<>();
        StringBuilder builder = new StringBuilder();

        for (char ch : text.toCharArray()) {
    
            TrieNode node = currentNode.getChildren().get(ch);
            // 匹配到敏感词
            if (node != null) {
    
                // 匹配到就加入到builder中
                builder.append(ch);
                // 判断该敏感词是否为最后一个
                if (node.getEndOfWord()) {
    
                    set.add(builder.toString());
                    // 如果只要返回第一个敏感词,则直接返回
                    if (isFirst) {
    
                        break;
                    }
                }
                // 切换到下一个节点
                currentNode = node;
            } else {
    
                // 未匹配到敏感词,从根节点重新开始
                currentNode = root;
                // 因为没有匹配到敏感词,所以之前加入的字符需要清空
                builder.delete(0, builder.length());
            }
        }
        return set;
    }

    /** * 替换敏感词 */
    public String replace(String text, String replaceChar) {
    
        Set<String> all = findAll(text);
        for (String word : all) {
    
            // 获取敏感词长度
            String myReplace = replaceStr(replaceChar, word.length());
            text = text.replaceAll(word, myReplace);
        }
        return text;
    }

    /** * 返回需要替换的敏感词字符 */
    private static String replaceStr(String replaceChar, Integer length) {
    
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < length; i++) {
    
            builder.append(replaceChar);
        }
        return builder.toString();
    }


}

4. 测试

敏感词:笨蛋坏人TMD

@Test
public void test_myUtil() {
    
    String text = "你TMD真是个坏人,笨蛋啊啊啊啊";
    String first = SensitiveUtil.X.findFirst(text);
    System.out.println("返回字符串中第一个敏感词====>>>>" + first);


    Set<String> all = SensitiveUtil.X.findAll(text);
    System.out.println("返回字符串中所有敏感词====>>>>" + all);

    String replace = SensitiveUtil.X.replace(text, "*");
    System.out.println("返回打马后字符串====>>>>" + replace);
}
返回字符串中第一个敏感词====>>>>TMD
返回字符串中所有敏感词====>>>>[坏人, TMD, 笨蛋]
返回打马后字符串====>>>>***真是个****啊啊啊啊

我是1024,一个专注Java技术、记录生活的博主。

欢迎扫描二维码关注“1024公众号”,一起学习,一起进步,看更多路,少走弯路。

. . .

相关推荐

额外说明

设计模式-责任链模式

职责链模式 职责链模式 职责链模式解决什么问题? 职责链模式实现 职责链模式 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象练成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止 职责链模式解决什么问题? 如请假

额外说明

使用大华汇智双目半球网络摄像机DH-IPC-HD4140X-E2获取流量统计

记录一下使用Java的SpringBoot+大华SDK在智慧公厕项目中使大华惠智双目半球网络摄像机DH-IPC-HD4140X-E2获取人流量统计数据 首先根据说明书登录摄像头,一般摄像头都有自己的账号和密码(可能是admin admin 也可能是adm

额外说明

ajax框架

ajax框架   Ajax框架介绍   到此为止,你可能已经注意到,使用Ajax编程时有很多麻烦事。如果你要支持多个浏览器(现在还有谁只支持一个浏览器呢?),无疑会遭遇不兼容问题。单看一个简单的动作,比如说创建XMLHttpRequest对象的一个实例,

额外说明

IBM db2 Procedure 无法删除

db2的存储过程,在开发中心中无法删除的处理办法 db2正常情况下,在存储过程中是可以删除的。但在异常的情况下, 会出现无法删除的现象。删除的时候,会报死锁或超时的提示。 在这样的情况下,处理办法是, 先用db2 force applications 

额外说明

Vue项目实战——【基于 Vue3.x + Vant UI】实现一个多功能记账本(登录注册页面,验证码)

基于 Vue3.x + Vant UI 的多功能记账本(四) 文章目录 基于 Vue3.x + Vant UI 的多功能记账本(四) 项目演示 1、登录注册页面 2、图片验证码 3、修改 axios 4、写到最后(附源码) 系列内容 参考链接 基于 Vu

额外说明

C语言刷题笔记(1)

第一题  这一题乍看之下以为是在考数组定义的相关知识,但实际考察的点为转义符合的知识  而放入\8则只会存入最后一个字符8 第二题  该题选择c选项 首先此题通过swap进行混淆,使得做题者主观认为应该是swap中经过计算得到的值。(出题陷阱) 其次本题

额外说明

剪切板编程遇到的不能粘贴的问题

         今天调试代码的时候遇到了一个奇怪的问题,粘贴功能出异常了,无法进行粘帖操作了。于是查阅了相关的代码,才发现原因:使用OpenClipboard打开剪切板后,没有调用CloseClipboard把剪切板关闭掉。所以在做剪切板相关编程时,一

额外说明

[Ext JS]3.8 树状表格TreeGrid

Ext JS并没有单独提供类似TreeGrid这种类型的组件, 而是直接使用 Ext.tree.Panel 就可以达成需要的效果, 有树形结构的Grid的效果如下图: 树形结构的Grid 的实现方式 树形结构的Grid虽然显示上更接近Grid ,但是其定

额外说明

由Job threw an unhandled exception和No qualifying bean of type ‘x‘ available引发对spring jdk和cglib动态代理的思考

文章目录 1. 复现错误 2. 分析错误 3. 解决问题 3.1 解决方法一 3.2 解决方法二 4. 分析spring中的jdk和cglib的动态代理 4.1 动态代理对比 4.2 原理区别 4.3 性能区别 4.4 各自局限 4.5 静态代理和动态的

额外说明

wordpress 自定义_获得WordPress网站自定义徽标的8个最佳地方

WordPress 定制 您想为您的 WordPress 网站获得自定义徽标吗?自定义徽标有助于建立您的品牌并使您的网站脱颖而出。在本文中,我们将向您展示一些在少量预算内为您的网站获得自定义徽标的最佳位置。 您想为您的 WordPress 网站获得自定义

ads via 小工具