PHP敏感词DFA算法实现

wen PHP项目 2

本文目录导读:

PHP敏感词DFA算法实现

  1. 基础DFA类实现
  2. 使用示例
  3. 高级版本(支持更多特性)
  4. 性能优化版本
  5. 使用建议
  6. 关键特性:

我来为您提供一个完整的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";
}

关键特性:

  1. 高效匹配:DFA算法时间复杂度为O(n),n为文本长度
  2. 内存优化:使用数组结构,共享前缀路径
  3. 支持中文:正确处理UTF-8编码
  4. 可扩展性:支持添加自定义逻辑(如跳过字符、拼音匹配等)

这个实现包含了基础DFA、高级特性和性能优化版本,可以根据实际需求选择合适的版本使用。

抱歉,评论功能暂时关闭!