DFA有穷自动机敏感词过滤算法

news2024/11/26 16:28:49

1.EndType

package com.example.utils.wordfilter;

/**
 * 结束类型定义
 */
public enum EndType {

    /**
     * 有下一个,结束
     */
    HAS_NEXT, IS_END
}

2.WordType

package com.example.utils.wordfilter;

/**
 * 词汇类型
 */
public enum WordType {

    /**
     * 黑名单/白名单
     */
    BLACK, WHITE
}

3.FlagIndex

package com.example.utils.wordfilter;

import java.util.List;

/**
 * 敏感词标记
 */
public class FlagIndex {

    /**
     * 标记结果
     */
    private boolean flag;
    /**
     * 是否黑名单词汇
     */
    private boolean isWhiteWord;
    /**
     * 标记索引
     */
    private List<Integer> index;

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public List<Integer> getIndex() {
        return index;
    }

    public void setIndex(List<Integer> index) {
        this.index = index;
    }

    public boolean isWhiteWord() {
        return isWhiteWord;
    }

    public void setWhiteWord(boolean whiteWord) {
        isWhiteWord = whiteWord;
    }
}

4.WordContext

package com.example.utils.wordfilter;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;

/**
 * 词库上下文环境
 * 初始化敏感词库,将敏感词加入到HashMap中,构建DFA算法模型
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class WordContext {

    /**
     * 敏感词字典
     */
    private final Map wordMap = new HashMap();

    /**
     * 是否已初始化
     */
    private boolean init;
    /**
     * 黑名单列表
     */
    private final String blackList;
    /**
     * 白名单列表
     */
    private final String whiteList;

    public WordContext() {
        this.blackList = "/blacklist.txt";
        this.whiteList = "/whitelist.txt";
        initKeyWord();
    }

    public WordContext(String blackList, String whiteList) {
        this.blackList = blackList;
        this.whiteList = whiteList;
        initKeyWord();
    }

    /**
     * 获取初始化的敏感词列表
     *
     * @return 敏感词列表
     */
    public Map getWordMap() {
        return wordMap;
    }

    /**
     * 初始化
     */
    private synchronized void initKeyWord() {
        try {
            if (!init) {
                // 将敏感词库加入到HashMap中
                addWord(readWordFile(blackList), WordType.BLACK);
                // 将非敏感词库也加入到HashMap中
                addWord(readWordFile(whiteList), WordType.WHITE);
            }
            init = true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 读取敏感词库,将敏感词放入HashSet中,构建一个DFA算法模型:<br>
     * 中 = { isEnd = 0 国 = {<br>
     * isEnd = 1 人 = {isEnd = 0 民 = {isEnd = 1} } 男 = { isEnd = 0 人 = { isEnd = 1 }
     * } } } 五 = { isEnd = 0 星 = { isEnd = 0 红 = { isEnd = 0 旗 = { isEnd = 1 } } } }
     */
    public void addWord(Iterable<String> wordList, WordType wordType) {
        Map nowMap;
        Map<String, String> newWorMap;
        // 迭代keyWordSet
        for (String key : wordList) {
            nowMap = wordMap;
            for (int i = 0; i < key.length(); i++) {
                // 转换成char型
                char keyChar = key.charAt(i);
                // 获取
                Object wordMap = nowMap.get(keyChar);
                // 如果存在该key,直接赋值
                if (wordMap != null) {
                    nowMap = (Map) wordMap;
                } else {
                    // 不存在则构建一个map,同时将isEnd设置为0,因为他不是最后一个
                    newWorMap = new HashMap<>(4);
                    // 不是最后一个
                    newWorMap.put("isEnd", String.valueOf(EndType.HAS_NEXT.ordinal()));
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }

                if (i == key.length() - 1) {
                    // 最后一个
                    nowMap.put("isEnd", String.valueOf(EndType.IS_END.ordinal()));
                    nowMap.put("isWhiteWord", String.valueOf(wordType.ordinal()));
                }
            }
        }
    }

    /**
     * 在线删除敏感词
     *
     * @param wordList 敏感词列表
     * @param wordType 黑名单 BLACk,白名单WHITE
     */
    public void removeWord(Iterable<String> wordList, WordType wordType) {
        Map nowMap;
        for (String key : wordList) {
            List<Map> cacheList = new ArrayList<>();
            nowMap = wordMap;
            for (int i = 0; i < key.length(); i++) {
                char keyChar = key.charAt(i);

                Object map = nowMap.get(keyChar);
                if (map != null) {
                    nowMap = (Map) map;
                    cacheList.add(nowMap);
                } else {
                    return;
                }

                if (i == key.length() - 1) {
                    char[] keys = key.toCharArray();
                    boolean cleanable = false;
                    char lastChar = 0;
                    for (int j = cacheList.size() - 1; j >= 0; j--) {
                        Map cacheMap = cacheList.get(j);
                        if (j == cacheList.size() - 1) {
                            if (String.valueOf(WordType.BLACK.ordinal()).equals(cacheMap.get("isWhiteWord"))) {
                                if (wordType == WordType.WHITE) {
                                    return;
                                }
                            }
                            if (String.valueOf(WordType.WHITE.ordinal()).equals(cacheMap.get("isWhiteWord"))) {
                                if (wordType == WordType.BLACK) {
                                    return;
                                }
                            }
                            cacheMap.remove("isWhiteWord");
                            cacheMap.remove("isEnd");
                            if (cacheMap.size() == 0) {
                                cleanable = true;
                                continue;
                            }
                        }
                        if (cleanable) {
                            Object isEnd = cacheMap.get("isEnd");
                            if (String.valueOf(EndType.IS_END.ordinal()).equals(isEnd)) {
                                cleanable = false;
                            }
                            cacheMap.remove(lastChar);
                        }
                        lastChar = keys[j];
                    }

                    if (cleanable) {
                        wordMap.remove(lastChar);
                    }
                }
            }
        }
    }

    /**
     * 读取敏感词库中的内容,将内容添加到set集合中
     */
    private Set<String> readWordFile(String file) throws Exception {
        Set<String> set;
        // 字符编码
        String encoding = "UTF-8";
        try (InputStreamReader read = new InputStreamReader(
                this.getClass().getResourceAsStream(file), encoding)) {
            set = new HashSet<>();
            BufferedReader bufferedReader = new BufferedReader(read);
            String txt;
            // 读取文件,将文件内容放入到set中
            while ((txt = bufferedReader.readLine()) != null) {
                set.add(txt);
            }
        }
        // 关闭文件流
        return set;
    }
}

5.WordFilter

package com.example.utils.wordfilter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 敏感词过滤器
 */
@SuppressWarnings("rawtypes")
public class WordFilter {

    /**
     * 敏感词表
     */
    private final Map wordMap;

    /**
     * 构造函数
     */
    public WordFilter(WordContext context) {
        this.wordMap = context.getWordMap();
    }

    /**
     * 替换敏感词
     *
     * @param text 输入文本
     */
    public String replace(final String text) {
        return replace(text, 0, '*');
    }

    /**
     * 替换敏感词
     *
     * @param text   输入文本
     * @param symbol 替换符号
     */
    public String replace(final String text, final char symbol) {
        return replace(text, 0, symbol);
    }

    /**
     * 替换敏感词
     *
     * @param text   输入文本
     * @param skip   文本距离
     * @param symbol 替换符号
     */
    public String replace(final String text, final int skip, final char symbol) {
        char[] charset = text.toCharArray();
        for (int i = 0; i < charset.length; i++) {
            FlagIndex fi = getFlagIndex(charset, i, skip);
            if (fi.isFlag()) {
                if (!fi.isWhiteWord()) {
                    for (int j : fi.getIndex()) {
                        charset[j] = symbol;
                    }
                } else {
                    i += fi.getIndex().size() - 1;
                }
            }
        }
        return new String(charset);
    }

    /**
     * 是否包含敏感词
     *
     * @param text 输入文本
     */
    public boolean include(final String text) {
        return include(text, 0);
    }

    /**
     * 是否包含敏感词
     *
     * @param text 输入文本
     * @param skip 文本距离
     */
    public boolean include(final String text, final int skip) {
        boolean include = false;
        char[] charset = text.toCharArray();
        for (int i = 0; i < charset.length; i++) {
            FlagIndex fi = getFlagIndex(charset, i, skip);
            if(fi.isFlag()) {
                if (fi.isWhiteWord()) {
                    i += fi.getIndex().size() - 1;
                } else {
                    include = true;
                    break;
                }
            }
        }
        return include;
    }

    /**
     * 获取敏感词数量
     *
     * @param text 输入文本
     */
    public int wordCount(final String text) {
        return wordCount(text, 0);
    }

    /**
     * 获取敏感词数量
     *
     * @param text 输入文本
     * @param skip 文本距离
     */
    public int wordCount(final String text, final int skip) {
        int count = 0;
        char[] charset = text.toCharArray();
        for (int i = 0; i < charset.length; i++) {
            FlagIndex fi = getFlagIndex(charset, i, skip);
            if (fi.isFlag()) {
                if(fi.isWhiteWord()) {
                    i += fi.getIndex().size() - 1;
                } else {
                    count++;
                }
            }
        }
        return count;
    }

    /**
     * 获取敏感词列表
     *
     * @param text 输入文本
     */
    public List<String> wordList(final String text) {
        return wordList(text, 0);
    }

    /**
     * 获取敏感词列表
     *
     * @param text 输入文本
     * @param skip 文本距离
     */
    public List<String> wordList(final String text, final int skip) {
        List<String> wordList = new ArrayList<>();
        char[] charset = text.toCharArray();
        for (int i = 0; i < charset.length; i++) {
            FlagIndex fi = getFlagIndex(charset, i, skip);
            if (fi.isFlag()) {
                if(fi.isWhiteWord()) {
                    i += fi.getIndex().size() - 1;
                } else {
                    StringBuilder builder = new StringBuilder();
                    for (int j : fi.getIndex()) {
                        char word = text.charAt(j);
                        builder.append(word);
                    }
                    wordList.add(builder.toString());
                }
            }
        }
        return wordList;
    }

    /**
     * 获取标记索引
     *
     * @param charset 输入文本
     * @param begin   检测起始
     * @param skip    文本距离
     */
    private FlagIndex getFlagIndex(final char[] charset, final int begin, final int skip) {
        FlagIndex fi = new FlagIndex();

        Map current = wordMap;
        boolean flag = false;
        int count = 0;
        List<Integer> index = new ArrayList<>();
        for (int i = begin; i < charset.length; i++) {
            char word = charset[i];
            Map mapTree = (Map) current.get(word);
            if (count > skip || (i == begin && Objects.isNull(mapTree))) {
                break;
            }
            if (Objects.nonNull(mapTree)) {
                current = mapTree;
                count = 0;
                index.add(i);
            } else {
                count++;
                if (flag && count > skip) {
                    break;
                }
            }
            if ("1".equals(current.get("isEnd"))) {
                flag = true;
            }
            if ("1".equals(current.get("isWhiteWord"))) {
                fi.setWhiteWord(true);
                break;
            }
        }

        fi.setFlag(flag);
        fi.setIndex(index);

        return fi;
    }
}

注:Skip距离

程序会跳过不同的距离,查找敏感词,距离越长,过滤越严格,效率越低,开发者可以根据具体需求设置,这里以“紧急”为敏感词举例,以此类推

  • 0 匹配紧急

  • 1 匹配不紧不急,紧x急

  • 2 匹配紧急,紧x急,紧xx急

6.功能测试

package com.example.wordfilter;

import com.example.utils.wordfilter.WordContext;
import com.example.utils.wordfilter.WordFilter;
import com.example.utils.wordfilter.WordType;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Collections;

/**
 * DFA敏感词单元测试用例
 */
@SpringBootTest
public class DFATest {

    /**
     * 词库上下文环境
     */
    private final WordContext context = new WordContext();
    private final WordFilter filter = new WordFilter(context);

    /**
     * 测试替换敏感词
     */
    @Test
    public void replace() {
        String text = "利于上游行业发展的政策逐渐发布";
        System.out.println(filter.replace(text));   //利于上游行业发展的政策逐渐发布
        context.removeWord(Collections.singletonList("上游行业"), WordType.WHITE);
        System.out.println(filter.replace(text));   //利于上**业发展的政策逐渐发布
    }

    /**
     * 测试是否包含敏感词
     */
    @Test
    public void include() {
        String text = "利于上游行业发展的政策逐渐发布";
        System.out.println(filter.include(text));   //false
        context.removeWord(Collections.singletonList("上游行业"), WordType.WHITE);
        System.out.println(filter.include(text));   //true
    }

    /**
     * 获取敏感词数
     */
    @Test
    public void wordCount() {
        String text = "利于上游行业发展的政策逐渐发布";
        System.out.println(filter.wordCount(text));   //0
        context.removeWord(Collections.singletonList("上游行业"), WordType.WHITE);
        System.out.println(filter.wordCount(text));   //1
    }

    /**
     * 获取敏感词列表
     */
    @Test
    public void wordList() {
        String text = "利于上游1行业发展的政策逐渐发布";
        System.out.println(filter.wordList(text));   //[]]
        context.removeWord(Collections.singletonList("上游行业"), WordType.WHITE);
        System.out.println(filter.wordList(text));   //[游行]
    }

    /**
     * 测试替换敏感词
     */
    @Test
    public void replace2() {
        String text = "你是个笨蛋吧";
        System.out.println(filter.replace(text));
    }

    /**
     * 测试替换敏感词
     */
    @Test
    public void replace3() {
        String text = "你是个日本鬼子吧";
        System.out.println(filter.replace(text));
    }

}

7.工程目录


可以点个免费的赞吗!!!   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1399882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS-WebAPIs-本地存储(五)

• 本地存储介绍 以前我们页面写的数据一刷新页面就没有了&#xff0c;是不是&#xff1f;随着互联网的快速发展&#xff0c;基于网页的应用越来越普遍&#xff0c;同时也变的越来越复杂&#xff0c;为了满足各种各样的需求&#xff0c;会经常 性在本地存储大量的数据&#xf…

python类继承之__init__函数覆盖问题

目录 1.问题描述 2.代码演示 3.总结 在Python这个广受欢迎的编程语言中&#xff0c;类继承是一项强大而精巧的特性。通过类继承&#xff0c;我们可以构建出更加灵活、可重用和易维护的代码&#xff0c;同时实现代码的模块化和扩展性。 但是如果对于熟悉C和java的人而言&…

Flink实战之运行架构

本文章&#xff1a;重点是分析清楚运行架构以及并行度与slot的分配 1、JobManager和TaskManager Flink中的节点可以分为JobManager和TaskManager。 JobManager处理器也称为Master&#xff0c;用于协调分布式任务执行。他们用来调度task进行具体的任务。TaskManager处理器也称…

蓝桥杯真题(Python)每日练Day2

题目 题目分析 对于本题首先确定其数据结构为优先队列&#xff0c;即邮费最小的衣服优先寄&#xff0c;算法符合贪心算法。可以直接使用queue库的PriorityQueue方法实现优先队列。关于PriorityQueue的使用方法主要有&#xff1a; import queue q queue.Queue()# 队列 pq qu…

【JavaEE】文件操作 —— IO

文件操作 —— IO 1. 文件的属性 文件内容文件大小文件路径文件名称 2. 文件的管理 采用树形结构进行管理。 3. 文件路径 分为两种&#xff1a;相对、绝对路径。 相对路径&#xff1a;相对于当前位置的路径&#xff0c;以“./xxx.xxx”为标志绝对路径&#xff1a;以从盘符…

【工具使用-win10最近使用的文件】怎样关闭win10系统最近使用的文件功能

一&#xff0c;简介 有时候我们不想打开文件管理器的时候&#xff0c;就出现之前访问过的文件或者文件夹&#xff0c;本文就介绍在win10系统下如何关闭“最近使用文件”的功能。 二&#xff0c;操作步骤 打开资源管理器&#xff0c;点击“选项” 按照如下步骤操作&#xff…

树结构实战,获取文件夹大小

文件IO是一个耗时操作&#xff0c;要尽量避免频繁读取磁盘。 而我们需要分析磁盘的占用空间&#xff0c;无法避免需要读取&#xff0c;但是期望只通过一次文件IO操作&#xff0c;来获取到所有某个目录下所有文件夹的信息。 所以需要一种方式可以仅进行一轮磁盘操作&#xff0…

【Linux】安装n卡驱动以及可能遇到的问题

文章目录 1.换源以及更新2.安装依赖3. 安装n卡驱动独显与核显切换nvidia-settings消失忘记安装依赖无法进入图形化界面的急救命令行无响应办法 1.换源以及更新 目前&#xff0c;换源完全只需要鼠标点点点就可以完成了&#xff0c;打开应用列表里的Software & Updates&…

vue3-模版引用

模版引用 ref 属性 场景&#xff1a;需要直接访问底层 DOM 元素。 方法&#xff1a;使用特殊的 ref 属性。 <input ref"input">ref 属性 允许我们在一个特定的 DOM 元素或子组件实例被挂载后&#xff0c;获得对它的直接引用。 访问模板引用 小 Demo: 当 i…

rk1126, 实现 yolov8 目标检测

基于 RKNN 1126 实现 yolov8 目标检测 Ⓜ️ RKNN 模型转换 ONNX yolo export model./weights/yolov8s.pt formatonnx导出 RKNN 这里选择输出 concat 输入两个节点 onnx::Concat_425 和 onnx::Concat_426 from rknn.api import RKNNONNX_MODEL ./weights/yolov8s.onnxRKNN_MOD…

flink operator 拉取阿里云私有镜像(其他私有类似)

创建 k8s secret kubectl --namespace flink create secret docker-registry aliyun-docker-registry --docker-serverregistry.cn-shenzhen.aliyuncs.com --docker-usernameops_acr1060896234 --docker-passwordpasswd --docker-emailDOCKER_EMAIL注意命名空间指定你使用的 我…

使用 vsCode创建GO项目

最近回顾了一下go的使用&#xff1a;具体操作看下面的参考连接&#xff0c;下面只描述一些踩过的坑&#xff1a; 1. go安装配置 安装go->配置go环境变量 推荐官网下载&#xff0c;速度很快&#xff1b; 这里需要配置五个参数&#xff1a;GOPATH/GOROOT/Path、GO111MODULE/…

GitHub提交 / 拉取时 443 fatal: unable to access ‘https:

这个问题嘛 懂得都懂 但是用了魔法后依旧会出现443错误 排查了工具发现并不是工具的问题 修改一下git代理即可解决 解决方法如下 确保魔法可用的情况下 打开魔法 打开系统设置 > 网络和Internet > 代理 找到自己的代理IP 如下 这里以我的代理IP和端口举例 在…

【工具】使用ssh进行socket5代理

文章目录 shellssh命令详解正向代理&#xff1a;反向代理&#xff1a;本地 socks5 代理 shell ssh -D 3333 root192.168.0.11 #输入密码 #3333端口已经使用远程机进行转发设置Windows全局代理转发 socks127.0.0.1 3333如果远程机为公网ip&#xff0c;可通过搜索引擎查询出网…

【C语言】数据在内存中的存储知识点(1)

一、整数在内存中的存储 二、大小端字节序和字节序判断 三、浮点数在内存中的存储

Linux的IO文件操作和文件系统

前要&#xff1a;本次我想给您带来关于 IO 和文件的知识&#xff0c;而文件在本系列中分为内存上的文件和磁盘上的文件。 1.文件概念 1.1.文件读写 在谈及系统接口之前&#xff0c;我们先来从 C 语言的角度来谈及一些前要知识&#xff0c;以辅助我们后续来理解系统 IO。 我们…

burp靶场--文件上传

burp靶场–文件上传 https://portswigger.net/web-security/file-upload/lab-file-upload-remote-code-execution-via-web-shell-upload 1.文件上传 1、原理&#xff1a;文件上传漏洞是指Web服务器允许用户将文件上传到其文件系统&#xff0c;而不充分验证文件的名称、类型、…

Shell 脚本实现自动启动程序、日志管理和定时任务监控

简介 本篇将通过Shell 脚本实现自动启动Java程序、日志管理和定时任务监控。脚本启动程序具灵活定制、可移植性和扩展性强的优点&#xff0c;可以根据需要添加额外的功能、配置选项和自定义行为&#xff0c;从而满足更具体的要求。 脚本编写 vim start_program.sh#!/bin/bas…

Lambda支持的方法引用

目录 引用类中的静态方法替换lambda引用对象实例化方法替换lambda引用类中的实例方法替换lambda引用构造器替换lambda 引用类中的静态方法替换lambda 引用类方法&#xff1a;引用类的静态方法&#xff1b;类名::静态方法名 demo: 将String类型数据转换成为Integer类型 创建一个…

MySQL 8.3 发布, 它带来哪些新变化?

1月16号 MySQL 官方发布 8.3 创新版 和 8.0.36 长期支持版本 (该版本 没有新增功能&#xff0c;更多是修复bug )&#xff0c;本文基于 官方文档 说一下 8.3 版本带来的变化。 一 增加的特性 1.1 GTID_NEXT 支持增加 TAG 选项。 之前的版本中 GTID_NEXTUUID:number &#xff…