本文目录导读:

我来为您提供一个完整的PHP敏感词DFA(确定性有限自动机)算法实现:
基础DFA类实现
<?php
class SensitiveWordFilter
{
private $dict = [];
private $replaceChar = '*';
/**
* 添加敏感词到DFA树
* @param string $word 敏感词
*/
public function addWord($word)
{
$word = trim($word);
if (empty($word)) {
return;
}
$len = mb_strlen($word, 'UTF-8');
$tree = &$this->dict;
for ($i = 0; $i < $len; $i++) {
$char = mb_substr($word, $i, 1, 'UTF-8');
if (!isset($tree[$char])) {
$tree[$char] = [];
}
$tree = &$tree[$char];
}
// 标记为敏感词结束
$tree['end'] = true;
}
/**
* 批量添加敏感词
* @param array $words 敏感词数组
*/
public function addWords(array $words)
{
foreach ($words as $word) {
$this->addWord($word);
}
}
/**
* 检查文本中是否包含敏感词
* @param string $text 待检查文本
* @return array 返回找到的敏感词数组
*/
public function check($text)
{
$foundWords = [];
$textLen = mb_strlen($text, 'UTF-8');
for ($i = 0; $i < $textLen; $i++) {
$tree = $this->dict;
$word = '';
$continueFlag = false;
for ($j = $i; $j < $textLen; $j++) {
$char = mb_substr($text, $j, 1, 'UTF-8');
// 过滤空白字符(可选)
if (trim($char) === '' && $continueFlag) {
$continueFlag = false;
continue;
}
if (!isset($tree[$char])) {
break;
}
$word .= $char;
$tree = $tree[$char];
if (isset($tree['end']) && $tree['end']) {
$foundWords[] = $word;
// 可选:跳过后续字符
$i = $j;
}
$continueFlag = true;
}
}
return array_unique($foundWords);
}
/**
* 过滤敏感词
* @param string $text 待过滤文本
* @param string $replaceChar 替换字符
* @return array ['text' => 过滤后文本, 'found' => 找到的敏感词]
*/
public function filter($text, $replaceChar = '*')
{
$replaceCharArr = mb_str_split($replaceChar, 1, 'UTF-8');
$filteredText = $text;
$foundWords = [];
$textLen = mb_strlen($text, 'UTF-8');
$chars = mb_str_split($text, 1, 'UTF-8');
for ($i = 0; $i < $textLen; $i++) {
$tree = $this->dict;
$word = '';
$lastMatchPos = -1;
$lastMatchLen = 0;
for ($j = $i; $j < $textLen; $j++) {
if (!isset($chars[$j])) {
break;
}
$char = $chars[$j];
if (!isset($tree[$char])) {
break;
}
$word .= $char;
$tree = $tree[$char];
if (isset($tree['end']) && $tree['end']) {
$lastMatchPos = $j;
$lastMatchLen = strlen($word);
}
}
if ($lastMatchPos >= 0 && $lastMatchLen > 0) {
// 找到敏感词
$sensitiveWord = mb_substr($text, $i, $lastMatchLen, 'UTF-8');
$foundWords[] = $sensitiveWord;
// 替换敏感词
$replaceStr = str_repeat($replaceChar, mb_strlen($sensitiveWord, 'UTF-8'));
$filteredText = mb_substr($filteredText, 0, $i, 'UTF-8')
. $replaceStr
. mb_substr($filteredText, $i + $lastMatchLen, null, 'UTF-8');
// 跳过已处理的字符
$i = $lastMatchPos;
}
}
return [
'text' => $filteredText,
'found' => array_unique($foundWords)
];
}
/**
* 获取DFA树结构(用于调试)
*/
public function getDict()
{
return $this->dict;
}
/**
* 清空词典
*/
public function clear()
{
$this->dict = [];
}
}
// 辅助函数:将字符串拆分为字符数组
if (!function_exists('mb_str_split')) {
function mb_str_split($string, $split_length = 1, $encoding = null)
{
if ($split_length <= 0) {
return [];
}
$encoding = $encoding ?: mb_internal_encoding();
$result = [];
$strLength = mb_strlen($string, $encoding);
for ($i = 0; $i < $strLength; $i += $split_length) {
$result[] = mb_substr($string, $i, $split_length, $encoding);
}
return $result;
}
}
使用示例
// 创建实例
$filter = new SensitiveWordFilter();
// 添加敏感词
$filter->addWord('赌博');
$filter->addWord('毒品');
$filter->addWord('色情');
$filter->addWord('暴力');
// 或者批量添加
$filter->addWords(['赌博', '毒品', '色情', '暴力', '违法', '犯罪']);
// 从文件加载敏感词
function loadWordsFromFile($filter, $filePath)
{
$words = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$filter->addWords($words);
}
// 检查文本
$text = "这是一个包含赌博和毒品的例子,还有色情内容";
$found = $filter->check($text);
print_r($found);
// 输出: Array ( [0] => 赌博 [1] => 毒品 [2] => 色情 )
// 过滤敏感词
$result = $filter->filter("这是一个包含赌博和毒品的例子,还有色情内容", '*');
echo $result['text']; // 这是一个包含**和**的例子,还有**内容
print_r($result['found']); // Array ( [0] => 赌博 [1] => 毒品 [2] => 色情 )
高级版本(支持更多特性)
<?php
class AdvancedSensitiveWordFilter extends SensitiveWordFilter
{
private $skipChars = [' ', ' ', '\t', '\n', '@', '#', '$', '%', '^', '&', '*', '-', '_', '+', '='];
private $enableSkipChar = true;
private $enablePinyinMatch = false;
/**
* 设置是否启用跳过字符
*/
public function setSkipCharEnabled($enabled)
{
$this->enableSkipChar = $enabled;
}
/**
* 添加自定义跳过字符
*/
public function addSkipChar($char)
{
$this->skipChars[] = $char;
}
/**
* 改进的检查方法,支持跳过字符
*/
public function check($text)
{
$foundWords = [];
$textLen = mb_strlen($text, 'UTF-8');
$chars = mb_str_split($text, 1, 'UTF-8');
for ($i = 0; $i < $textLen; $i++) {
$tree = $this->dict;
$word = '';
$skipCount = 0;
for ($j = $i; $j < $textLen; $j++) {
$char = $chars[$j];
// 跳过字符处理
if ($this->enableSkipChar && in_array($char, $this->skipChars)) {
$skipCount++;
if ($skipCount > 3) { // 最多跳过3个字符
break;
}
continue;
}
if (!isset($tree[$char])) {
break;
}
$word .= $char;
$tree = $tree[$char];
if (isset($tree['end']) && $tree['end']) {
$foundWords[] = $word;
}
}
}
return array_unique($foundWords);
}
/**
* 从JSON文件加载敏感词
*/
public function loadFromJson($jsonPath)
{
if (!file_exists($jsonPath)) {
throw new Exception("File not found: $jsonPath");
}
$data = json_decode(file_get_contents($jsonPath), true);
if (isset($data['words'])) {
$this->addWords($data['words']);
}
}
/**
* 导出词典为JSON(可用于保存)
*/
public function exportToJson()
{
return json_encode(['words' => $this->getAllWords()], JSON_UNESCAPED_UNICODE);
}
/**
* 获取所有敏感词(需要遍历DFA树)
*/
public function getAllWords()
{
$words = [];
$this->traverseDict($this->dict, '', $words);
return $words;
}
/**
* 递归遍历DFA树获取所有敏感词
*/
private function traverseDict($node, $prefix, &$words)
{
foreach ($node as $char => $subTree) {
if ($char === 'end') {
$words[] = $prefix;
continue;
}
$this->traverseDict($subTree, $prefix . $char, $words);
}
}
/**
* 智能过滤(支持上下文感知)
*/
public function smartFilter($text)
{
$result = $this->filter($text, '*');
// 可以在这里添加更多智能处理逻辑
// 保留首尾字符、部分替换等
return $result;
}
}
性能优化版本
<?php
class HighPerformanceSensitiveWordFilter
{
private $dict = [];
private $cache = [];
private $cacheSize = 1000;
/**
* 使用数组引用进行更高效的操作
*/
public function addUserDict($word)
{
$charCount = mb_strlen($word, 'UTF-8');
$p = &$this->dict;
for ($i = 0; $i < $charCount; $i++) {
$char = mb_substr($word, $i, 1, 'UTF-8');
if (!isset($p[$char])) {
$p[$char] = [];
}
$p = &$p[$char];
}
if (!isset($p['end'])) {
$p['end'] = true;
}
}
/**
* 带缓存的高性能过滤
*/
public function filterWithCache($text)
{
// 检查缓存
if (isset($this->cache[$text])) {
return $this->cache[$text];
}
$result = $this->filter($text, '*');
// 管理缓存大小
if (count($this->cache) >= $this->cacheSize) {
array_shift($this->cache);
}
$this->cache[$text] = $result;
return $result;
}
/**
* 高效过滤方法(避免mb_substr重复调用)
*/
public function fastFilter($text, $replaceChar = '*')
{
$textLen = mb_strlen($text, 'UTF-8');
if ($textLen === 0) {
return ['text' => $text, 'found' => []];
}
$chars = mb_str_split($text, 1, 'UTF-8');
$result = '';
$found = [];
$i = 0;
while ($i < $textLen) {
$tree = $this->dict;
$matched = false;
$j = $i;
while ($j < $textLen) {
$char = $chars[$j];
if (!isset($tree[$char])) {
break;
}
$tree = $tree[$char];
$j++;
if (isset($tree['end'])) {
$word = implode('', array_slice($chars, $i, $j - $i));
$found[] = $word;
$result .= str_repeat($replaceChar, $j - $i);
$i = $j;
$matched = true;
break;
}
}
if (!$matched) {
$result .= $chars[$i];
$i++;
}
}
return [
'text' => $result,
'found' => array_unique($found)
];
}
}
使用建议
// 使用示例
$filter = new AdvancedSensitiveWordFilter();
// 加载敏感词库
$filter->addWords([
'赌博', '毒品', '色情', '暴力',
'诈骗', '传销', '赌博网址', '代购毒品'
]);
// 测试过滤
$text = "这是一个包含赌博和毒品的内容,还有赌博网址";
$result = $filter->smartFilter($text);
echo $result['text']; // 输出过滤后的文本
print_r($result['found']); // 输出找到的敏感词
// 批量处理
$texts = [
'文本1包含赌博',
'文本2正常内容',
'文本3包含毒品和色情'
];
foreach ($texts as $text) {
$result = $filter->filterWithCache($text);
echo $result['text'] . "\n";
}
关键特性:
- 高效匹配:DFA算法时间复杂度为O(n),n为文本长度
- 内存优化:使用数组结构,共享前缀路径
- 支持中文:正确处理UTF-8编码
- 可扩展性:支持添加自定义逻辑(如跳过字符、拼音匹配等)
这个实现包含了基础DFA、高级特性和性能优化版本,可以根据实际需求选择合适的版本使用。