【Golang】DFA算法过滤敏感词Golang实现

news2024/12/23 14:47:01

什么是DFA算法

DFA全称:Deterministic Finite Automaton,翻译过来就是确定性有限自动机,其特征是,有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态,但是确定性有穷自动机不会从同一状态触发的两个边标志由相同的符号。
通俗的讲DFA算法就是把你要匹配的做成一颗字典树,然后对你输入的内容进行匹配的过程

如何构建这颗字典树呢

这是一颗简单字典树的,我们的第一步就是构建出一个这样的包含敏感词的树在这里插入图片描述
下面我说一下构建过程
每个节点的结构

// 定义一个Node结构体,代表DFA的一个节点。
type Node struct {
	End  bool   // End字段表示是否为一个单词的结束。
	Next map[rune]*Node   // Next字段是一个映射,用于存储此节点的所有子节点。
}
// 定义一个DFAMatcher结构体,代表一个完整的DFA。
type DFAMatcher struct {
	replaceChar rune   // replaceChar字段是替换敏感词的字符。
	root        *Node   // root字段是DFA的根节点。
}

我们要先创捷出一个root节点,在root节点中是不存放数据的

//创建出一个DFA树的根节点实例
func NewDFAMather() *DFAMatcher {
	return &DFAMatcher{
		root: &Node{
			End: false,
		},
	}
}

在确定完节点的结构后,我们需要跟据敏感词来构建这颗字典树

// Build方法用于构建DFA,它会将提供的所有单词添加到DFA中。
func (d *DFAMatcher) Build(words []string) {
	for _, item := range words { // 遍历提供的所有单词。
		d.root.AddWord(item) // 将每一个单词添加到DFA的根节点。
	}
}

// AddWord方法用于向当前节点添加一个单词。
// 这个方法会遍历单词的每一个字符,并为每一个字符添加一个子节点。
func (n *Node) AddWord(word string) {
	node := n                     // 从当前节点开始。
	chars := []rune(word)         // 将字符串转化为rune类型的切片,以便处理Unicode字符。
	for index, _ := range chars { // 遍历单词的每一个字符。
		node = node.AddChild(chars[index]) // 递归地为每一个字符添加子节点。
	}
	node.End = true // 设置最后一个节点为单词的结束。
}

// AddChild方法向当前节点添加一个子节点。
// 如果子节点已经存在,它将不会被重复添加。
func (n *Node) AddChild(c rune) *Node {
	if n.Next == nil { // 如果Next字段为nil,则初始化一个映射。
		n.Next = make(map[rune]*Node)
	}
	//检查字符c是否已经是当前节点的子节点。
	if next, ok := n.Next[c]; ok { // 如果ok为true,则字符c已经是当前节点的子节点,直接返回该子节点。
		return next
	} else { // 否则,创建一个新的节点,并将其设置为当前节点的子节点。
		n.Next[c] = &Node{
			End:  false,
			Next: nil,
		}
		return n.Next[c] // 返回新创建的子节点。
	}
}

根据上面的代码就可一构建出一颗包含你传入的敏感词的树,在这颗树种根节点不存放数据

过滤关键词

下面就是跟据你传入的内容来过滤敏感词了,你可以把敏感词替换成其他字符,也可以统计敏感词的个数,这就看你自己需要什么了
下面是代码实现

// Match方法用于在文本中查找并替换敏感词。
// 它返回找到的敏感词列表和替换后的文本。
func (d *DFAMatcher) Match(text string) (sensitiveWords []string, replaceText string) {
	if d.root == nil { // 如果DFA是空的,直接返回原始文本。
		return nil, text
	}
	textChars := []rune(text)                     // 将文本转化为rune类型的切片,以便处理Unicode字符。
	textCharsCopy := make([]rune, len(textChars)) // 创建一个文本字符的副本,用于替换敏感词。
	copy(textCharsCopy, textChars)                // 复制原始文本字符到副本。
	length := len(textChars)                      // 获取文本的长度。
	for i := 0; i < length; i++ {                 // 遍历文本的每一个字符。
	// 在DFA树中查找当前字符对应的子节点
		temp := d.root.FindChild(textChars[i])
		if temp == nil {
			continue // 如果不存在匹配,继续检查下一个字符
		}
		j := i + 1
		// 遍历文本中的字符,查找匹配的敏感词,第一个匹配上了,就进行后面的向下匹配
		for ; j < length && temp != nil; j++ {
			if temp.End {
				// 如果找到一个敏感词,将其添加到结果列表中,并在副本中替换为指定字符
				sensitiveWords = append(sensitiveWords, string(textChars[i:j]))
				replaceRune(textCharsCopy, '*', i, j) //替换敏感词
			}
			temp = temp.FindChild(textChars[j])
		}
		// 处理文本末尾的情况,如果末尾是一个完整的敏感词,添加到结果列表中,并在副本中替换为指定字符
		if j == length && temp != nil && temp.End {
			sensitiveWords = append(sensitiveWords, string(textChars[i:length]))
			replaceRune(textCharsCopy, '*', i, length)
		}
	}
	return sensitiveWords, string(textCharsCopy) // 返回匹配到的敏感词列表和替换后的文本

}

// FindChild方法用于在当前节点的子节点中查找一个特定的子节点。
func (n *Node) FindChild(c rune) *Node {
	if n.Next == nil { // 如果Next字段为nil,则直接返回nil。
		return nil
	}
	//检查字符c是否是当前节点的子节点。
	if _, ok := n.Next[c]; ok { // 如果ok为true,则字符c是当前节点的子节点,返回该子节点。
		return n.Next[c]
	}
	return nil // 否则,返回nil。
}

//替换掉文章中出现的关键词
func replaceRune(chars []rune, replaceChar rune, begin int, end int) {
	for i := begin; i < end; i++ {
		chars[i] = replaceChar
	}
}

以上就是使用Golang代码实现了一个简单的DFA算法过滤敏感词的一个算法,这个算法相对于其他的性能更好,匹配更快。

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

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

相关文章

java 常见api Arrays类

int类型数组 package daysreplace;import java.util.Arrays;public class Test {public static void main(String[] args) {int[] arrays{38,24,42,56,22,44};//直接输出数组名称就是内存地址System.out.println(arrays);//Arrays.toString()会将数组内容转成字符串形式System…

6款好用良心的国产软件,每一款都是精品,电脑秒变黑科技

在如今科技发展迅猛的时代&#xff0c;我们在工作中基本都会使用到电脑&#xff0c;其实电脑上有很多非常实用的软件&#xff0c;能够提高我们的工作效率。今天给大家分享6款良心好用的国产软件&#xff0c;每一款都是精品&#xff0c;让你电脑秒变黑科技。 01、滴答清单 滴答清…

mac文件为什么不能拖进U盘?

对于Mac用户来说&#xff0c;可能会遭遇一些烦恼&#xff0c;比如在试图将文件从Mac电脑拖入U盘时&#xff0c;却发现文件无法成功传输。这无疑给用户带来了很大的不便。那么&#xff0c;mac文件为什么不能拖进U盘&#xff0c;看完这篇你就知道了。 一、U盘的读写权限问题 如果…

17795-2019 建筑绝热用玻璃棉制品 思维导图

声明 本文是学习GB-T 17795-2019 建筑绝热用玻璃棉制品.pdf而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了建筑绝热用玻璃棉制品的分类和标记、技术要求、试验方法、检验规则以及标志、包 装、运输和贮存。 本标准适用于建筑围…

归并排序与非比较排序详解

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; &#x1f354;前言&#xff1a; 上篇博客我们讲解了非常重要的快速排序&#xff0c;相信大家已经学会了。最后我们再学习一种特殊的排序手法——归并排序。话不多说我们直接上菜。 目录 归并排序 基本思想 递归思路…

Altium Designer培训 | 2 - 原理图库创建篇

目录 原理图界面屏幕放大&缩小&移动 元件库介绍及电阻容模型的创建 【SCH Library】面板 元件符号 绘制一只电阻的模型 设置栅格大小 绘制一只电容的模型 IC类元件模型的创建 排针类元件模型的创建 光耦及二极管元件模型 现有元件模型的调用 参考上一篇文章…

10.6数构(概念,优先队列复习,漏斗倒水时间期望,小木棍dfs,括号匹配,后缀表达式,PTA第三题)

选择应试 数据项是数据的最小单位 数据的逻辑结构与数据元素本身的内容和形式无关 带头结点的单循环链表中&#xff0c;任一结点的后继结点的指针域均不空 顺序存储结构的主要缺点是不利于插入或删除操作 顺序存储方式不仅能用于存储线性结构&#xff0c;还可以用来存放非…

【juc】countdownlatch实现并发网络请求

目录 一、截图示例二、代码示例2.1 测试代码2.2 接口代码 一、截图示例 二、代码示例 2.1 测试代码 package com.learning.countdownlatch;import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate;import java.util.Arrays; import java.uti…

基于SSM的药房药品采购集中管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

【UE5 Cesium】15-Cesium for Unreal 加载本地地形

目录 一、加载全球无高度地形 二、加载区域DEM 效果 一、加载全球无高度地形 1. 先去如下网址下载全球无高度地形&#xff1a;Using a global terrain layer without height detail - #9 by RidhwanAziz - Cesium for Unreal - Cesium Community 下载后如下&#xff1a; 解…

【Spring Cloud系统】- Zookeer特性与使用场景

【Spring Cloud系统】- Zookeer特性与使用场景 一、概述 Zookeeper是一个分布式服务框架&#xff0c;是Apache Hadoop的一个子项目&#xff0c;它主要是用来解决分布式应用中经常遇到的一些数据管理问题。如&#xff1a;统一命名服务、状态同步服务、集群管理、分布式应用配置…

华为云云耀云服务器L实例评测|Ubuntu 22.04部署edusoho-ct企培版教程 | 支持华为云视频点播对接CDN加速

华为云云耀云服务器L实例评测&#xff5c;Ubuntu 22.04部署edusoho企培版教程 1、选择购买 华为云耀云服务器L实例 简单上云第一步 2、选择你要安装的操作系统&#xff0c;例如 Ubuntu 22.04 server 64bit 3、然后支付订单就行了 4、华为云云耀云服务器L实例创建好之后&#x…

2023年台州市第三届网络安全技能大赛(MISC)—Black Mamba

前言&#xff1a;当时比赛没有做出来现在来复现一下 就当记录一下&#xff08;这个思路没想到&#xff09; Black Mamba&#xff1a; 一张图片 常规得分离&#xff0c;属性&#xff0c;LSB&#xff0c;盲水印等都尝试过 无果&#xff01; 考点&#xff1a;异或解密&#xff0…

一篇理解TCP协议

一、TCP协议概念。 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的传输层协议。它主要用于在计算机网络中&#xff0c;通过建立可靠的通信连接来进行数据传输。 TCP协议的特点如下&#xff1a; 可靠性&#xf…

满足你甜食需求的葡萄酒是怎样的?

也许这是不言而喻的&#xff0c;但我们认为&#xff0c;如果没有一杯完美的葡萄酒来补充你最喜爱的菜肴的复杂风味&#xff0c;一顿美食就不完整。无论您是享用美味的葡萄酒作为开胃菜&#xff0c;还是搭配主菜&#xff0c;我们相信我们最喜爱的饮料是一餐中任何部分的完美补充…

ESP32设备驱动-TFT_eSPI显示中文

TFT_eSPI显示中文 文章目录 TFT_eSPI显示中文1、安装TFT_eSPI库2、创建字库3、生成字库头文件4、使用字库本文将详细介绍如何使用TFT_eSPI显示中文。 1、安装TFT_eSPI库 2、创建字库 TFT_eSPI字体工具使用Processing软件创建字体。 下载并安装Processing:https://processin…

css的gap设置元素之间的间隔

在felx布局中可以使用gap来设置元素之间的间隔&#xff1b; .box{width: 800px;height: auto;border: 1px solid green;display: flex;flex-wrap: wrap;gap: 100px; } .inner{width: 200px;height: 200px;background-color: skyblue; } <div class"box"><…

6-5 头插法创建单链表(C) 分数 10

struct Node* buildLinkedList(int* arr, int n) {//创建哨兵位struct Node* head (struct Node*)malloc(sizeof(struct Node));head->link NULL;struct Node* node NULL;for (int i 0; i < n; i){//循环创建每一个结点node (struct Node*)malloc(sizeof(struct Nod…

Android系统定制之监听USB键盘来判断是否弹出软键盘

一.项目背景 在设备上弹出软键盘,会将一大部分UI遮挡起来,造成很多图标无法看到和点击,使用起来不方便,因此通过插入usb键盘输入代替软键盘,但是点击输入框默认会弹出软键盘,因此想要插入USB键盘时,默认关闭软键盘,拔出键盘时再弹出,方便用户使用 二.设计思路 2.1…

Python大数据之PySpark(六)RDD的操作

文章目录 RDD的操作函数分类Transformation函数Action函数基础练习[Wordcount快速演示]Transformer算子 -*- coding: utf-8 -*-Program function&#xff1a;完成单Value类型RDD的转换算子的演示1-创建SparkContext申请资源2-key和value类型算子groupByKey[(b, <pyspark.res…