独立游戏《星尘异变》UE5 C++程序开发日志8——实现敏感词过滤功能(AC自动机)
在游戏中经常会有需要玩家输入一些内容的功能,例如聊天,命名等,这款游戏只有在存档时辉用到命名功能,所以这个过滤也只是一个实验性的功能,我们将使用AC自动机来实现,这是在我们把“csdn”这个词设置为屏蔽词后的效果:


目录
一、敏感词词典的处理
二、搭建AC自动机
1.自动机节点的数据机构
2.加载词典
3.建立字典树
4.建立失配指针
三、替换字符串中的敏感词
一、敏感词词典的处理
我们是从别的地方找的开源词典,所以要做一下筛选,首先我们要去重,然后去除所有的标点符号空格和其他无关字符,然后同时去掉长度为1的字符,因为其会在AC自动机中表现的过于严格
wifstream InputTxt;
wofstream OutputTxt;
//词典的路径,这里是单独开了一个程序,所以和后面项目里相关代码用到的路径不同
InputTxt.open("Dict.txt", ios::out);
//使用宽字符串读入
wstring Word;
map<wstring, bool>Words;
while (getline(InputTxt,Word))
{//去重if (Words.find(Word) == Words.end()){//去掉短字,但这里对中文无效,因为一个中文字长度大概率不为1if (Word.size() == 1)continue;for (auto& It1 : Word){//统一成小写if (iswupper(It1)){It1 = towlower(It1);}//去除字符 if (iswpunct(It1)||iswblank(It1)||iswspace(It1)){Word.erase(It1);It1--;} }//记录这个词处理完毕Words[Word] = true;}
}
InputTxt.close();
OutputTxt.open("Dict.txt", ios::out);
//将处理完的词重新写入词典
for (auto& It: Words)
{OutputTxt << It.first << endl;
}
二、搭建AC自动机
AC自动机就是在字典树的基础上加入了类似于KMP的失配指针,当匹配串在树上失配时,会回溯到某个上一层的节点,该节点的所有父节点即前缀,和失配节点的所有父节点的后缀,形成最大匹配,使多模匹配的效率达到近似O(匹配串长度)
1.自动机节点的数据机构
因为我们要将匹配到的敏感词替换成'*',所以相比于一般的自动机节点,要在每个词的末尾记录这个词的长度,同时因为不止26个字母,所以也用红黑树替代了数组
class FSensitiveWordFilterStruct
{
public:FSensitiveWordFilterStruct()=default;explicit FSensitiveWordFilterStruct(const wchar_t&InputCharacter):Character(InputCharacter){};//字符wchar_t Character{'#'};//匹配的字符串的长度int Length{0};//子节点TMap<wchar_t,std::shared_ptr<FSensitiveWordFilterStruct>>ChildNode;//失配指针FSensitiveWordFilterStruct* FailPointer{this};
};
然后我们在游戏实例中声明自动机的根节点:
//屏蔽词过滤器树根
std::shared_ptr <FSensitiveWordFilterStruct>SensitiveWordFilterRoot;
在游戏启动时初始化AC自动机,用到的函数后面一个一个讲:
UAstromutateGameInstance::UAstromutateGameInstance()
{//加载词典LoadTXTFile("/Movies/Dict.txt");//实例化自动机根节点SensitiveWordFilterRoot=std::make_shared<FSensitiveWordFilterStruct>(FSensitiveWordFilterStruct());//将词典中的词添加到树上for(const auto&It:*SensitiveWords){AddWordToSensitiveWordTree(It);}//建立失配指针InitializeSensitiveWordTree();
}
2.加载词典
这里我们把词典作为txt文件放在Movies文件夹下,因为该文件夹中的所有文件都会被原封不动的打包,我们将所有敏感词存到一个TArray中
//声明敏感词词典
TSharedPtr<TArray<FString>> SensitiveWords;
auto UAstromutateGameInstance::LoadTXTFile(const FString& Path)->void
{//获取词典路径FString Temp{FPaths::ProjectContentDir()+Path};//实例化词典数组SensitiveWords=MakeShared<TArray<FString>>(TArray<FString>());//加载所有词FFileHelper::LoadFileToStringArray(*SensitiveWords,*Temp);UE_LOG(LogTemp,Warning,TEXT("SensitiveWords loade %d Words"),SensitiveWords->Num());
}
3.建立字典树
从根节点开始,遍历模式串,如果当前点没有当前字符对应的子节点,就创建之,然后无论有无都移动到该子节点
auto UAstromutateGameInstance::AddWordToSensitiveWordTree(const FString& InputString) const->void
{//获取根节点FSensitiveWordFilterStruct* Temp=SensitiveWordFilterRoot.get();//遍历模式串中的每一个字符for(const auto&It:InputString){wchar_t CurrentChar{It};//如果当前点没有对应的子节点,就添加之if(!Temp->ChildNode.Contains(CurrentChar)){Temp->ChildNode.Add(CurrentChar,std::make_shared<FSensitiveWordFilterStruct>(FSensitiveWordFilterStruct(CurrentChar)));}Temp=Temp->ChildNode[CurrentChar].get();}//将词的长度记录在词尾Temp->Length=InputString.Len();
}
4.建立失配指针
因为失配指针指向的节点一定在当前点的上层,所以我们进行bfs,首先将根节点的所有直连的子节点的失配指针指向根节点,因为这些点的上层节点只有根节点。然后对于一个失配点,如果其父节点的失配指针指向的点的子节点中有和该失配点相同的点,则失配点的失配指针指向该点,否则指向根节点
auto UAstromutateGameInstance::InitializeSensitiveWordTree() const -> void
{//bfs队列std::queue<std::shared_ptr<FSensitiveWordFilterStruct>>Queue;//将深度为1的点的失配指针指向根节点for(auto&It:SensitiveWordFilterRoot->ChildNode){It.Value->FailPointer=SensitiveWordFilterRoot.get();Queue.push(std::make_shared<FSensitiveWordFilterStruct>(*It.Value));}while(!Queue.empty()){std::shared_ptr<FSensitiveWordFilterStruct> CurrentNode=Queue.front();Queue.pop();//遍历所有子节点for(auto&It:CurrentNode->ChildNode){//父节点的失配指针指向的节点是否含有匹配的子节点if(!CurrentNode->FailPointer->ChildNode.Contains(It.Key)){It.Value->FailPointer=SensitiveWordFilterRoot.get();}else{It.Value->FailPointer=CurrentNode->FailPointer->ChildNode[It.Key].get();}Queue.push(std::make_shared<FSensitiveWordFilterStruct>(*It.Value));}}
}
三、替换字符串中的敏感词
首先我们将玩家输入的字符串使用字典中字符串同样的方法进行处理,去除符号和空格,全部转为小写,然后遍历其每一个字符,不匹配就按失配指针移动,匹配就检查是否是词尾,如果是的话根据记录的词的长度算出这个词的区间,将这个居间内的所有字符替换成'*',该操作不会影响到后面的匹配,最后将字符串还原成原来有符号和空格的格式并返回
auto UAstromutateGameInstance::ReplaceSensitiveWords(const FString& RawString)->FString
{FString Result{""};//对玩家输入的字符串进行处理for(const auto&It:RawString){if(iswpunct(It)||iswblank(It)||iswspace(It))continue;if(isupper(It))Result+=towlower(It);elseResult+=It;}FSensitiveWordFilterStruct* Temp{SensitiveWordFilterRoot.get()};//遍历匹配串的每一个字符for(int i=0;i<Result.Len();i++){wchar_t CurrentChar{Result[i]};//如果失配就一直回溯,直到根节点while(!Temp->ChildNode.Contains(CurrentChar)&&Temp!=SensitiveWordFilterRoot.get()){Temp=Temp->FailPointer;}//仍然适配就结束这个字符的搜索if(!Temp->ChildNode.Contains(CurrentChar)){Temp=SensitiveWordFilterRoot.get();continue;}//移动到匹配的节点Temp=Temp->ChildNode[CurrentChar].get();FSensitiveWordFilterStruct* Temp2{Temp};//遍历匹配到的所有词while(Temp2!=SensitiveWordFilterRoot.get()){if(Temp2->Length){//根据长度算出该词其实位置for(int j=i-Temp2->Length+1;j<=i;j++){Result[j]='*';}}Temp2=Temp2->FailPointer;}}//将处理完的字符串还原成输入的格式FString TrueResult{RawString};int CurrentIndex{0};for(auto&It:TrueResult){if(iswpunct(It)||iswblank(It)||iswspace(It))continue;if(iswupper(It)&&iswlower(Result[CurrentIndex])){continue;}It=Result[CurrentIndex++];}return TrueResult;
}
相关文章:
独立游戏《星尘异变》UE5 C++程序开发日志8——实现敏感词过滤功能(AC自动机)
在游戏中经常会有需要玩家输入一些内容的功能,例如聊天,命名等,这款游戏只有在存档时辉用到命名功能,所以这个过滤也只是一个实验性的功能,我们将使用AC自动机来实现,这是在我们把“csdn”这个词设置为屏蔽…...
使用 Swagger 在 Golang 中进行 API 文档生成
Swagger 是一款强大的 API 文档生成工具,可以帮助开发者轻松创建、管理和展示 RESTful API 文档。在本文中,我们将介绍如何在 Golang 项目中使用 Swagger 来生成 API 文档。 官网地址 : gin-swagger 前提条件 Golang 开发环境(…...
Pip换源实战指南:加速你的Python开发
1. Pip换源的重要性 在使用Python进行软件开发或数据分析时,pip 是Python的包管理工具,用于安装和管理第三方库。然而,由于网络环境的差异,特别是在某些国家,访问默认的PyPI(Python Package Indexÿ…...
【数据结构】常用数据结构的介绍:理解与应用
文章目录 前言一、介绍二、使用场景三、总结 前言 在计算机科学中,数据结构是我们组织和存储数据的方式,它可以帮助我们高效地执行各种操作,如搜索、插入和删除。从数组和链表,到树和图,不同的数据结构有着不同的优点…...
【优秀python系统毕设】基于Python flask的气象数据可视化系统设计与实现,有LSTM算法预测气温
第一章 绪论 1.1 研究背景 在当今信息爆炸的时代,气象数据作为重要的环境信息资源,扮演着关键的角色。然而,传统的气象数据呈现方式存在信息量庞大、难以理解的问题,限制了用户对气象信息的深入理解和利用。因此,基…...
【康复学习--LeetCode每日一题】2951. 找出峰值
题目: 给你一个下标从 0 开始的数组 mountain 。你的任务是找出数组 mountain 中的所有 峰值。 以数组形式返回给定数组中 峰值 的下标,顺序不限 。 注意: 峰值 是指一个严格大于其相邻元素的元素。 数组的第一个和最后一个元素 不 是峰值。…...
PYTHON学习笔记(八、字符串及的使用)
目录 1、字符串 1.1、字符串的常用操作 1.2、格式化字符串 1.2.1、占位符格式化字符串 1.2.2、f-string格式化字符串 1.2.3、str.format( )格式化字符串 1.3、数据的验证 1.4、正则表达式 1.5.1元字符 1.5.2限定符 1.5.3其他字符 1.5.4re模块 1、字符串 1.1、字符…...
文件共享功能无法使用提示错误代码0x80004005【笔记】
环境情况: 其他电脑可以正常访问共享端,但有一台电脑访问提示错误代码0x80004005。 处理检查: 搜索里输入“启用或关闭Windows功能”按回车键,在“启用或关闭Windows功能”里将“SMB 1.0/CIFS文件共享支持”勾选后(故…...
FTP(File Transfer Protocal,文件传输协议)
文章目录 引言FTP管理工具FTP客户端FTP连接模式控制连接数据连接FTP命令/响应FTP命令FTP响应FTPSSFTP引言 FTP(File Transfer Protocal,文件传输协议)用于建立两台主机间的数据文件传输下载。使用客户/服务器(Client/Server)架构,基于TCP协议,服务端口为21。 FTP链接…...
DevEco Studio中使用Qt,编写HarmonyOS程序
文章目录 1.操作2.注意事项2.1.adapter_ts2.1.手机插到电脑后,DevEco无法识别 1.操作 最近需要尝试把之前在Windwos下用Qt实现的程序移植到鸿蒙(HarmonyOS)系统上。 我使用的DevEco版本是5.03.501 找了一下资料,官方࿰…...
基于单文档的MFC图像增强
目录 function.h ColorEnhanceDib.h ColorEnhanceDib.cpp Dib.h Dib.cpp FrequencyFilterDib.h FrequencyFilterDib.cpp GrayTransformDib.h GrayTransformDib.cpp HistogramDib.h HistogramDib.cpp SharpenProcessDib.h SharpenProcessDib.cpp SmoothProcessDib.h Sm…...
云计算实训13——DNS域名解析、ntp时间服务器配置、主从DNS配置、多区域DNS搭建
一、DNS域名解析 1.正向解析 将域名解析为IP地址 DNS正向解析核心配置 (1)安装bind [rootdns ~]# yum -y install bind (2)编辑配置文件 编辑named.conf文件,限定访问权限 [rootdns ~]# vim /etc/named.conf 编辑named.rfc文件,指定要访问的域名 [ro…...
【C#】Visual Studio2022打包依赖第三方库的winForm程序为exe
0.简介 IDE:VS2022 平台:C# .NetFramework4.7.2 WinForm界面 有GDAL、EEplus第三方库的依赖,所以在其他未安装环境的电脑中功能无法使用。 1. 安装 1.1 运行文件输出 在VS扩展中选择管理扩展,安装:Microsoft Visua…...
《算法笔记》总结No.11——数字处理(上)欧拉筛选
机试中存在部分涉及到较复杂数字的问题,这是编码的基本功,各位一定要得心应手。 目录 一.最大公约数和最小公倍数 1.最大公约数 2.最小公倍数 二.素数 1.判断指定数 2.输出所有素数 3.精进不休——埃拉托斯特尼筛法 4.达到更优!——…...
DP学习——享元模式
学而时习之,温故而知新。 享元模式 名词解析 有必要解释下“享元”两字,英文原文是flyweight pattern——轻量级模式,但是翻译过来的“享元”两字太牛逼了——褒贬不一,翻译的他妈都不认识。 享元的高雅在于: 享:共享/共用 元:…...
无人机10公里WiFi图传摄像模组,飞睿智能超清远距离无线监控,智能安防新潮流
在这个科技日新月异的时代,我们对影像的捕捉和传播有了更高的要求。从传统的有线传输到无线WiFi图传,每一次技术的飞跃都为我们带来了全新的视觉体验。今天,我们要探讨的,正是一款具有划时代意义的科技产品——飞睿智能10公里WiFi…...
SAP S/4HANA Cloud Public Edition
即装即用的云ERP软件。借助SaaS模式为企业提供完备、现代化的ERP 云套件,为企业带来新的技术突破,如自动化的业务流程与基于数据的商业分析。企业可选择这款智能云ERP软件,快速实现自身价值。 什么是 SAP S/4HANA Cloud Public Edition&#…...
LabVIEW汽车动态信号模拟系统
随着汽车工业的快速发展,对汽车电子控制单元(ECU)的测试与仿真需求日益增加。开发了一种基于LabVIEW软件开发的汽车动态信号模拟系统,该系统能有效模拟ECU在实车环境下的工作状态,为ECU的开发和测试提供了一个高效、经…...
chrome 插件:content-script 部分逻辑在页面无法生效,可考虑插入 script 到页面上
背景: 某页面有个输入框, 用的应该是什么库里的组件, 直接修改内容不生效/机制不明确, 于是使用 paste event 粘贴到输入框, 结果发现也不行 定位: 使用 mutationObserver , 发现事件确实触发了, 输入框内容变了, 但马上又变回来了, 于是怀疑是输入框组件有做 mutationObers…...
【前端 10】初探BOM
初探BOM:浏览器对象模型 在JavaScript的广阔世界中,BOM(Browser Object Model,浏览器对象模型)扮演着举足轻重的角色。它为我们提供了一套操作浏览器窗口及其组成部分的接口,让我们能够通过编写JavaScript…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
