Java 实现敏感词检测

news2024/11/25 7:15:37

敏感词检测

敏感词的检测,一般是建立一个敏感词库,然后判断字符串中是否存在敏感词库中的某些词汇,然后将其过滤或者替换显示为其他文本,这对于一个和谐的网络环境是及其必要的,接下来就我们看看敏感词检测的实现方式有哪些。

传统方式

对于传统的敏感词检测,一般是将全部的敏感词放在集合中,然后循环遍历检测敏感词:

List<String> sensitiveList = Arrays.asList("阿巴阿巴", "花姑娘", "吊毛吃猪肉");
String text="花姑娘吃猪肉";
for (String s : sensitiveList) {
    boolean hit = text.contains(s);
    System.out.println(hit);
}

很明显,这种实现方式虽然很简单,但是敏感词库过大时,一次判断就要进行几万甚至十几万次循环判断,这是很耗时的。因此我们引入了敏感词检测的第二种方式。

有限状态机(DFA)

在 DFA 中,给定一个输入序列,DFA 通过根据输入和当前状态查找状态转移函数中的规则来决定下一个状态,最终判断是否接受输入。具体是怎么弄的呢。简单来说就是将所有敏感词的前缀复用起来,构造前缀树,这棵树记录了敏感词库中所有词可能构成的全部状态。

前缀树

比如现在有几个敏感词:吊毛吃猪肉、吊毛很帅、你很帅、你很笨、ctm…我们可以将其按照树形结构,构造成这样:
在这里插入图片描述

在检测时,循环检测字符串,只要遇到有结束标识(图中绿色的部分),就表明存在敏感词,这样一来,不仅能够节省内存空间,还能够减少判断次数。

前缀树结构

作为一棵树,那么它的结构也是很简单的:

/**
     * 前缀树
     */
    private static class TrieNode {

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

        // 子节点(key是下级字符,value是下级节点)
        private final Map<Character, TrieNode> subNodes = new HashMap<>();
    }

构造前缀树

我们只需要获取全部敏感词,依次对每个字符进行判断,完成构造即可:

 /**
     * 将一个敏感词添加到前缀树中
     *
     * @param keyword 敏感词
     */
    private void addKeyword(String keyword) {
        TrieNode tempNode = ROOT_NODE;
        for (int i = 0; i < keyword.length(); i++) {
            char c = keyword.charAt(i);
            TrieNode subNode = tempNode.getSubNode(c);
            if (subNode == null) {
                // 初始化子节点
                subNode = new TrieNode();
                tempNode.addSubNode(c, subNode);
            }
            // 指向子节点,进入下一轮循环
            tempNode = subNode;
            // 设置结束标识
            if (i == keyword.length() - 1) {
                tempNode.setKeywordEnd(true);
            }
        }
    }

敏感词判断

当拿到待检测的字符串,我们需要遍历该字符串,然后从树的根节点开始,不断获取子节点,当前所处节点达到某一个节点的结束标识为true时,代表目前的位置是一串敏感词,将其替换为***;然后继续从下一个字符开始,将树的节点指向根节点,继续检测,直到字符串遍历完成,具体代码如下:

/**
 * 过滤敏感词
 *
 * @param text 待过滤的文本
 * @return 过滤后的文本
 */
public static String filter(String text) {
    if (StringUtils.isEmpty(text)) {
        return null;
    }
    // 指针1
    TrieNode tempNode = ROOT_NODE;
    // 指针2
    int begin = 0;
    // 指针3
    int position = 0;
    // 结果
    StringBuilder sb = new StringBuilder();
    while (position < text.length()) {
        char c = text.charAt(position);
        // 跳过符号
        if (isSymbol(c)) {
            // 若指针1处于根节点,将此符号计入结果,让指针2向下走一步
            if (tempNode == ROOT_NODE) {
                sb.append(c);
                begin++;
            }
            // 无论符号在开头或中间,指针3都向下走一步
            position++;
            continue;
        }
        // 检查下级节点
        tempNode = tempNode.getSubNode(c);
        if (tempNode == null) {
            // 以begin开头的字符串不是敏感词
            sb.append(text.charAt(begin));
            // 进入下一个位置
            position = ++begin;
            // 重新指向根节点
            tempNode = ROOT_NODE;
        } else if (tempNode.isKeywordEnd()) {
            // 发现敏感词,将begin~position字符串替换掉
            sb.append(REPLACEMENT);
            // 进入下一个位置
            begin = ++position;
            // 重新指向根节点
            tempNode = ROOT_NODE;
        } else {
            // 检查下一个字符
            position++;
        }
    }
    // 将最后一批字符计入结果
    sb.append(text.substring(begin));
    return sb.toString();
}

/**
 * 判断是否为符号
 *
 * @param c 字符
 * @return 判断
 */
private static boolean isSymbol(Character c) {
    // 0x2E80~0x9FFF 是东亚文字范围
    return !isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
}

public static boolean isAsciiAlpha(char ch) {
    return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch);
}

public static boolean isAsciiAlphaUpper(char ch) {
    return ch >= 'A' && ch <= 'Z';
}

public static boolean isAsciiAlphaLower(char ch) {
    return ch >= 'a' && ch <= 'z';
}

public static boolean isAsciiNumeric(char ch) {
    return ch >= '0' && ch <= '9';
}

public static boolean isAsciiAlphanumeric(char ch) {
    return isAsciiAlpha(ch) || isAsciiNumeric(ch);
}

结语

到此,我们可以得出结论,使用构造前缀树来完成敏感词检测,比起传统的遍历集合检测,能够节省些许的内存空间,而且在查询效率上有了提升。但是依然还存在着一些小问题,比如不能够完成多模式匹配。

什么是多模匹配?

比如敏感词abc和bcd。
匹配字符串abcde。
单模匹配后:***de
多模匹配后:****e

如果有兴趣,可以继续下去研究一下。

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

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

相关文章

Spring MVC 简介

目录 1. 什么是MVC2. 什么是SpringMVC 1. 什么是MVC MVC是一种常用的软件架构模式。可以看作是一种设计模式&#xff0c;也可以看作是一种软件框架。经典MVC模式中&#xff0c;M是指模型&#xff0c;V是视图&#xff0c;C则是控制器&#xff0c;使用MVC的目的是将M和V的实现代…

最强自动化测试框架Playwright(11)- 录制视频

视频 使用playwright&#xff0c;您可以录制测试视频。 录制视频 视频在测试结束时在浏览器上下文关闭时保存。如果手动创建浏览器上下文&#xff0c;请确保等待 browser_context.close&#xff08;&#xff09;。 context browser.new_context(record_video_dir"vid…

【Java】 java | git | win系统重装会给开发环境带来哪些问题

一、概述 1、近期发现电脑用起来不丝滑了&#xff0c;文件夹操作卡顿&#xff0c;一阵操作还会蓝屏 2、不能忍&#xff0c;整理排查 二、电脑情况 1、CPU&#xff1a; I5-9400F 2.9GHz 6核 2、内存&#xff1a; 32G 3、固态&#xff1a;256G 4、机械&#xff1a;1T 5、盘符使用…

Java基础篇--Number Math 类

目录 Number类 扩展小知识 Math类 实例 Number类 Java中的Number类是一个抽象类&#xff0c;它是所有包装类&#xff08;如Integer、Double、Long等&#xff09;的父类。这个类提供了将基本数据类型&#xff08;如int、double、long等&#xff09;封装为对象&#xff0c;…

[Leetcode] [Tutorial] 多维动态规划

文章目录 62. 不同路径Solution 62. 不同路径 一个机器人位于一个 m ∗ * ∗ n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。 问总共有多少条不同的路径&#xff1f; 示例…

QT-K线效果显示

QT-K线效果显示 一、演示效果二、关键程序三、程序链接 一、演示效果 二、关键程序 代码如下&#xff1a; #include "kvolumegrid.h"#include <QMessageBox> #include <QPainter> #include <QPen> #include <QString>kVolumeGrid::kVolume…

OpenCV实例(八)车牌字符识别技术(二)字符识别

车牌字符识别技术&#xff08;二&#xff09;字符识别 1.字符识别原理及其发展阶段2.字符识别方法3.英文、数字识别4.车牌定位实例 1.字符识别原理及其发展阶段 匹配判别是字符识别的基本思想&#xff0c;与其他模式识别的应用非常类似。字符识别的基本原理就是对字符图像进行…

iphone拷贝照片中间带E自动去重软件,以及java程序如何打包成jar和exe

文章目录 一、前提二、问题描述三、原始处理方式四、程序处理4.1 java程序如何打包exe4.1.1 首先打包jar4.1.2 开始生成exe软件使用方式 4.2 更换图标4.2.1 更换swing的打包jar图标4.2.2 更换exe图标 4.2 附件下载 一、前提 用苹果手机照相&#xff0c;有不使用默认的4:3拍照的…

【大数据之Kafka】一、Kafka定义消息队列及基础架构

1 定义 Kafka传统定义&#xff1a;Kafka是一个分布式的基于发布/订阅模式的消息队列&#xff08;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域。 发布/订阅&#xff1a;消息的发布者不会将消息直接发送给特定的订阅者&#xff0c;而是将发布的消息分为…

React 组件防止冒泡方法

背景 在使用 antd 组件库开发时&#xff0c;发现点击一个子组件&#xff0c;却触发了父组件的点击事件&#xff0c;比如&#xff0c;我在一个折叠面板里面放入一个下拉框或者对下拉框列表渲染做定制&#xff0c;每个下拉框候选项都有一个子组件… 解决 其实这就是 Javascri…

头条移动端项目Day02—— app端文章查看、静态化freemarker、分布式文件系统minIO

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

Selenium之css怎么实现元素定位?

世界上最远的距离大概就是明明看到一个页面元素站在那里&#xff0c;但是我却定位不到&#xff01;&#xff01; Selenium定位元素的方法有很多种&#xff0c;像是通过id、name、class_name、tag_name、link_text等等&#xff0c;但是这些方法局限性太大&#xff0c; 随着自动…

《华为认证》L2TP VPN配置

配置接口ip地址&#xff0c;并且将防火墙的接口加入对应的安全区域 。 LNS的G1/0/0 IP为202.1.1.1 1、配置LNS的缺省路由&#xff1a; ip route-static 0.0.0.0 0.0.0.0 202.1.1.2 2、通过WEB 界面配置防火墙的 L2TP VPN 浏览器输入&#xff1a; https://202.1.1.1:8443/def…

fastApi基础

1、fastApi简介 官方文档&#xff1a;https://fastapi.tiangolo.com/ 源码&#xff1a; https://github.com/tiangolo/fastapi 2、环境准备 安装python 安装pycharm 安装fastAPI 安装 uvicorn 查看已经安装的第三方库&#xff1a;pip list 查看pip 配置信息&#xff1a;pip co…

大数据扫盲(1): 数据仓库与ETL的关系及ETL工具推荐

在数字化时代&#xff0c;数据成为了企业决策的关键支持。然而&#xff0c;随着数据不断增长&#xff0c;有效地管理和利用这些数据变得至关重要。数据仓库和ETL工具作为数据管理和分析的核心&#xff0c;将帮助企业从庞杂的数据中提取有价值信息。 一、ETL是什么&#xff1f; …

运维监控学习1

1、监控对象&#xff1a; 1、监控对象的理解&#xff1b;CPU是怎么工作的&#xff1b; 2、监控对象的指标&#xff1a;CPU使用率&#xff1b;上下文切换&#xff1b; 3、确定性能基准线&#xff1a;CPU负载多少才算高&#xff1b; 2、监控范围&#xff1a; 1、硬件监控&#x…

2023年淘宝京东直播方向,MCN机构申请入驻详细指南!

对于专注于孵化内容的直播主持人和在观望淘宝平台内容MCN的人来说&#xff0c;这是一个重大利好。尽管今后可能调整保证金标准&#xff0c;但目前看来&#xff0c;淘宝仍然有意引进和扶持更多的内容MCN机构。 随着《内容MCN机构管理规范》的推出&#xff0c;内容MCN机构、内容…

【Matlab智能算法】Elman神经网络-遗传算法(Elman-GA)函数极值寻优——非线性函数求极值

往期博客&#x1f449; 【Matlab】BP神经网络遗传算法(BP-GA)函数极值寻优——非线性函数求极值 【Matlab】GRNN神经网络遗传算法(GRNN-GA)函数极值寻优——非线性函数求极值 【Matlab】RBF神经网络遗传算法(RBF-GA)函数极值寻优——非线性函数求极值 本篇博客将主要介绍Elman神…

【C++深入浅出】初识C++上篇(关键字,命名空间,输入输出,缺省参数,函数重载)

目录 一. 前言 二. 什么是C 三. C关键字初探 四. 命名空间 4.1 为什么要引入命名空间 4.2 命名空间的定义 4.3 命名空间使用 五. C的输入输出 六. 缺省参数 6.1 缺省参数的概念 6.2 缺省参数的分类 七. 函数重载 7.1 函数重载的概念 7.2 函数重载的条件 7.3 C支…

【雕爷学编程】Arduino动手做(24)---水位传感器模块2

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…