现在的游戏有敏感词检测这一点,相信大家也不陌生了,不管是聊天,起名,签名还是简介,只要是能让玩家手动输入的地方,一定少不了敏感词识别,至于识别之后是拒绝修改还是星号替换,这个就各有各的做法了,但是绕不开的一定是需要高效的敏感词检测机制。
相信大家对于游戏里聊天框的以下内容已经不陌生了
- “我***”
- “你真牛*”
- “你是不是傻*”
一个垃圾的游戏环境是非常影响玩游戏的心情的,看到这些***,就知道游戏已经帮我们屏蔽掉了那些屏蔽字了,对于玩游戏而言,心里会好受很多。敏感词识别对于游戏的重要性不言而喻。当然,除了游戏,也有很多业务场景可能需要敏感词检测,如果你接到这样一个需求的时候,你会怎么做?
一、原生API
作为Java程序员,我的第一反应,一定是使用jdk原生的String类提供的contain或replace方法来进行包含判断或字符替换,这是最简单直接的方式。那我们就来看看String的实现方式:
contains
String在java中以char数组形式存储,而String.contains的实现,实际上是对数组的遍历查找匹配
// 最终调用方法static int indexOf(char[] source, int sourceOffset, int sourceCount,char[] target, int targetOffset, int targetCount,int fromIndex) { // ...}
replace
String.replace有4个接口,实现为正则匹配替换或直接遍历替换
public String replace(char oldChar, char newChar) { // 直接进行字符串遍历,替换第一个匹配的字符串}public String replace(CharSequence target, CharSequence replacement) { // 创建Pattern,使用LITERAL模式进行正则匹配替换replaceAll// 当设置LITERAL标志时,输入字符串中的所有字符都被视为普通字符。// 这意味着正则表达式的特殊字符,如点号(.)、星号(*)、加号(+)等,都将失去它们在正则表达式中的特殊意义,被直接视为普通字符。}public String replaceAll(String regex, String replacement) { // 创建Pattern,使用正则表达式模式匹配替换replaceAll}public String replaceFirst(String regex, String replacement) { // 创建Pattern,使用正则表达式模式匹配替换replaceFirst,仅替换第一个匹配的字符串}
通过jdk提供的String源码我们可以得到以下结果:
- 使用contains方法进行包含判断,它的底层实现原理其实就是通过遍历目标字符串的字符数组进行挨个匹配;少量敏感词检测的时候是可行的,但如果目标字符串很大,并且要匹配的敏感词足够多的时候,它的遍历匹配效率是很低的。
- replace则分两种实现,其中一种是类似contains方法,也是进行对目标字符串进行字符数组的遍历替换。
- replace的另一种实现,是通过java的正则表达式去做匹配,正则匹配相比于遍历匹配,效率上不会有明显提升,但对于复杂模式的解析匹配会有比较明显的优势
其他语言的字符串操作API大同小异,具体看源码的实现方式
二、正则表达式
另外一种我们能想到的方式就是进行正则表达式的匹配了。前面提到,在java中如果使用String的api,它有部分接口就是使用正则表达式来实现的。
使用正则表达式有一定优势,也有一定缺陷。这就不得不提正则表达式的实现原理:FA(Finite Automaton:有限自动机)
DFA与NFA
FA又分为DFA和NFA,我们以正则ab|ac举例
NFA(Nondeterministic finite automaton:非确定性有限状态自动机)
在NFA中表达式会构建为以下结构
- 非确定性:对于给定的输入符号,NFA可以从一个状态转移到多个状态。这意味着存在多种可能的状态转换路径,NFA在任何时间点都可以处于多个状态。
- 回溯:由于NFA在处理输入时可以选择多条路径,因此可能需要回溯。当某条路径未能达到接受状态时,NFA会返回并尝试其他可能的路径。
- 构造:NFA相对容易构造,特别是对于复杂的或包含多种可能的语言(例如正则表达式)。
- 运行效率:由于其非确定性特性,NFA在运行时可能需要更多的计算资源,特别是在处理长输入字符串时。
DFA(Deterministic finite automaton:确定性有限自动机)
在DFA中表达式会构建为以下结构
- 确定性:对于给定的输入符号,DFA从一个状态转移到另一个唯一确定的状态。这意味着DFA在任何时间点只能处于一个状态。
- 无回溯&