本人是一枚PHPer,工作期间学习过一些东西,也看过一些书,发现要把知识吸收为自己的能力,还是要多应用、总结。所以,下定决心,要把自己“毕生”所学、所实践写下来。一方面是为了自己提升能力;另一方面也是为做个记录,日后回顾。

本人写的所有东西都将与PHP服务端开发相关,也都将是工作过程中遇到的实际问题,并且在实际项目中所有应用。鉴于能力有限,若有写错之处,请多多包涵,恳请斧正,以免误人。

最近接触到一个用户留言数据处理的接口,其中一部分逻辑是对留言进行敏感词过滤。我看了下之前同事的代码,用的是正则匹配,将目标字符串放入循环对与敏感词依次匹配。好在目前数据量不大,这个任务暂时不存在太大问题,但始终不是长久之计。但是为了产品的长远发展,我想搞点新花样,决定用 DFA 算法重新做一下这个匹配逻辑。

“DFA(Deterministic Finite Automaton)就是确定有穷自动机,它是是通过...” 这种定义我就不写(chao)了,我看着都晕,下面我来讲讲我理解的DFA算法的计算过程(也不知道对不对)。

首先按照一般人的思维逻辑,看一下这个例子。假设给你一个词语 A(“中国”),让你判断是否在字符串 C(“我是中国人”)出现了。一般人的做法是,用眼睛瞄一下 C 里是否有 A 这个字串,然后得出结论 “C中出现了A”。这里的瞄一眼的逻辑过程应该是,先从C的第一个字往后看,直到看到A字符串。具体过程如下:

1、先将 C 的第一个字符 “我” 与 A 的第一个字符 “中” 进行对比,很明显 “我” 和 “中” 不相同

2、再对比 C 的第二个字符 “是” 与 A 的第一个字符,遗憾的是 “是” 和 “中” 也不相同

3、然后对比 C 的第三个字符 “中” 与 A 的第一个字符,可以看到这两个字符匹配上了

4、接着对比 C 的第四个字符 “国” 和 A 的第二个字符 “国”,很幸运又匹配成功

5、这个时候可以看到, A 已经到了最后一个字符了,也就是说在字符串 C 中找到了词语 A

上面的案例中的词语 A 可以看作是敏感词,字符串 C 就是用户输入的待过滤字符串。整个过程可以想象为,有一个指针(pa)指向待过滤字符串,然后依次往字符串尾部移动。同时将指针(pa)指向的字符,与敏感词的第一个字符进行对比。如果成功匹配,那就尝试对比指针(pa)后面(字符串尾部方向)的连续几个字符与敏感词对应的后续几个字符,直到敏感词的最后一个字符。

现实中,敏感词肯定不是一个词语,而是很多个词语。那怎样快速将指针(pa)指向的字符与多个敏感词的第一个字符进行对比呢?在 PHP 里可以将敏感词的第一个字符串放在一个数组里,然后用 isset等函数即可判断出指针指向的字符是否匹配上了其中一个敏感词的某个字符。具体的数据结构,可以看下图,比较好理解。

可以看到,数据结构里,还有一个 'end' 字段,这就是用于判断当前这个字符,是否是某个敏感词的结束位置。

数据结构的构建代码如下

/**

* 添加一个词到 hashmap 里

* @param string $_word 单个敏感词

*/

protected function addToTree($_word) {

$hashMap = &$this->__hashMap;

$wordLen = mb_strlen($_word, 'UTF-8');

for ($i = 0; $i < $wordLen; $i++) {

$char = mb_substr($_word, $i, 1, 'UTF-8');

if (array_key_exists($char, $hashMap)) {

//  hashmap 中已经存在

if ($i == ($wordLen - 1)) {

$hashMap[$char]['end'] = 1;

}

} else {

//  不存在于 hashmap 中

if ($i == ($wordLen - 1)) {

//  当前已经到了 敏感词 的最后一个字符了

$hashMap[$char] = [];

$hashMap[$char]['end'] = 1;

} else {

$hashMap[$char] = [];

}

}

$hashMap = &$hashMap[$char];

}

}

数据结构构建好了之后,就可以进行匹配逻辑了。匹配逻辑也比较简单,还是按照上面的例子来推导一下:

1、在 $tree 数组里查询,是否存在键为 C 的第一个字符 “我” .很明显,不存在的。

2、然后查询是否存在 C 的第二个字符 “是” 的键。假装很遗憾,也没有。

3、接着再查询一下 C 的第三个字符 “中” ,array_key_exists('中', $tree),那么可以看到结果喜人

4、这个时候因为要对比下一个字符了,我们弄两个中间变量 $tmpTree = $tree['中']; $curChar = '国'

5、重复上面的步骤,查询一下 $tmpTree 数组里是否有 '国' 这个键。答案是肯定的,而且 $tmpTree['国']['end'] == 1,说明已经到了敏感词的一个结束位置了

6、至此,成功匹配到一个敏感词,更多的敏感词匹配过程和上面一样

秉着能用就行的态度,我的代码是这样的:

/**

* 不间断(全)匹配

* @param type $_dfaTree    DFA敏感词数据

* @param type $_dstStr    目标字符串

* @param type $_matchAll  是否全匹配(false 时匹配成功一个敏感词就会返回结果)

*/

public function unbreakSearch($_dfaTree, $_dstStr, $_matchAll = true) {

//  匹配到的位置集合,[1,3] 表示从下标为 1-3 的匹配到了敏感词

$matchSet = [];

$dstStrLen = mb_strlen($_dstStr, 'UTF-8');

for ($header = 0; $header < $dstStrLen; $header++) {

$firstChar = mb_substr($_dstStr, $header, 1, 'UTF-8');

if (!array_key_exists($firstChar, $_dfaTree)) {

continue;

}

$matchTree = $_dfaTree[$firstChar];

for ($matchPos = $header + 1; true; $matchPos++) {

$curChar = mb_substr($_dstStr, $matchPos, 1, 'UTF-8');

if (!array_key_exists($curChar, $matchTree)) {

//  当前字符没有匹配上,直接结束本轮匹配即可

break;

}

//  当前字符已经匹配了,且这个字符还不是敏感词的结束位置

$matchTree = $matchTree[$curChar];

if (array_key_exists('end', $matchTree)) {

//  已经到达结束位置

$matchSet[] = [$header, $matchPos];

//  一次匹配

if (false === $_matchAll) {

return $matchSet;

}

break;

}

}

}

return $matchSet;

}

至此,核心的东西已经编完了。

里面其实还有很多细节,需要根据具体的业务需求去做权衡。比如要不要把目标字符串里的所有敏感词都筛选出来,还是匹配到一个就完事?要不要处理标点符号?是否要做不连续的匹配,也就是“我是中国的人”能否判定为匹配到敏感词“中国”?

文中的源码,可以在下面的仓库里找到,里面还有一个 demo 和更多注释、说明信息。

php dfa,DFA 算法的PHP实现相关推荐

  1. python实现dfa过滤算法_使用DFA实现文字过滤

    ------------------------------------- 大写字母是状态,小写字母是动作:我们可以看到S+a=U,U+a=Q,S+b=V等等.一般情况下我们可以用矩阵来表示整个状态转 ...

  2. python实现dfa过滤算法_Hopcroft算法DFA最小化Python实现

    DFA最小化原理 所谓自动机的化简问题即是对任何一个确定有限自动机DFA M,构造另一个确定有限自动机DFA M',有L(M)=L(M'),并且M'的状态个数不多于M的状态个数,而且可以肯定地说,能够 ...

  3. python实现dfa过滤算法_DFA 算法实现敏感词过滤(python 实现)

    敏感词过滤的经典算法DFA ,看完相关资料后,自己实现了一下,同时做了评估实验 先上代码 #!/usr/bin/python2.6 # -*- coding: utf-8 -*- import tim ...

  4. python实现dfa过滤算法_Python实现DFA算法,完成实体词匹配和敏感词过滤等功能

    一.什么是DFA算法 DFA 全称为:Deterministic Finite Automaton,即确定有穷自动机.其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个 ...

  5. [转]NFA/DFA算法

    作者:陈梓瀚  (http://www.cppblog.com/vczh/) 1.问题概述 随着计算机语言的结构越来越复杂,为了开发优秀的编译器,人们已经渐渐感到将词 法分析独立出来做研究的重要性.不 ...

  6. 算法-DFA算法-敏感词过滤算法(OC、Swift、Python)

    前言 前段时间,公司的IM SDK想做敏感词过滤,但是后端的小伙伴<比较忙>,在开产品需求会的时候想把敏感词过滤放到前端,让iOS.安卓自己搞,但是前端小伙伴写了一个方法来检测一段文本,耗 ...

  7. 敏感词之 DFA 算法

    敏感词之 DFA 算法 常用算法 遍历匹配 将输入的词语,与词库中的敏感词逐个字符遍历,对比是否包含 优点:思路简单,易于实现(KMP 算法,Brute-Force 算法) 缺点:当词库数目非常大时, ...

  8. 记录一次敏感词过滤算法DFA的应用案例

    目录 0. DFA是什么? 1.为什么要用DFA 2.DFA工具类实现 3.性能对比效果 3.1 普通关键字过滤 3.2 DFA关键字过滤 0. DFA是什么? 参考文档:敏感词过滤的算法原理之DFA ...

  9. 词法分析(4)---NFA与DFA的转化

    1. 子集构造(Subset Construction) 这是一个转换NFA到DFA的算法.我们知道NFA和DFA的区别最主要的就是一个状态和一个input symbol是否能够确定一个状态的问题,对 ...

  10. 正则表达式引擎的构建——基于编译原理DFA(龙书第三章)——1 概述

    说明:本系列文章介绍的算法均来自编译原理(龙书)一书,如果读者对代码没有兴趣,只想了解算法思路,完全可以阅读龙书相关章节内容,比我讲得清晰透彻. 序: 啃编译原理半年以来,任然徘徊在前4章,其间反反复 ...

最新文章

  1. 为元素绑定多个相同事件 绑定事件的另一种方式 复习 介绍 元素的事件绑定
  2. java 缘起_GraalVM 助力 Java 进入函数即服务时代
  3. 现代程序设计课程简介
  4. c语言输入输出重定向到串口,关于printf重定向到串口的问题分析 - 全文
  5. 【报告分享】2020年中国在线教育创新企业榜单.pdf(附下载链接)
  6. if语句中的赋值与判断
  7. return、reutrn false、e.preventDefault、e.stopPropagation、e.stopImmediatePropagation的区别
  8. DB2 8.2 9.1 9.5 9.7 下载地址(原创)
  9. STM32F429第四篇之跑马灯程序详解
  10. Python爬虫入门案例
  11. 录屏演示软件 ActivePresenter Pro v7.5.8 中文破解版
  12. Win7 FTP搭建
  13. 谷歌公开裸眼3D全息视频聊天技术:8k屏幕、4块GPU
  14. 2016 word 安装6.9b mathtype后,灰色不可用
  15. 字节跳动+阿里+华为+小米等10家大厂面试真题,已开源
  16. 再见安卓 你好鸿蒙,再见华为,你好鸿蒙
  17. D. Challenging Valleys
  18. [MTK]LCD 调试总结
  19. 尚硅谷YYDS (课件资料)
  20. 跟我一起学PyTorch-07:嵌入与表征学习

热门文章

  1. Futter基础第7篇: 实现底部导航
  2. console.log(iVal)是什么?显示在哪里?
  3. 【浅说】堆(heap)和栈(stack)区别
  4. JS 类数组,字符串,转换成数组的方法
  5. JS-WEB-API(BOM、DOM)
  6. Docker简介及Linux下安装
  7. 常用数字集成电路引脚图
  8. python做数据排序,python 实现对数据的排序和绘图
  9. 使用python连接eNSP中交换机并添加配置
  10. CICD详解(九)——gitlab简单使用