上路抗压,野爹常来

php过滤敏感词库算法

本人网上搜集的敏感词库

https://gitee.com/likxarlit/file_php.git

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/11/11
 * Time: 10:39
 */

class filterWords
{
    protected $dict;//敏感词字典

    public function __construct($arr)
    {
        $this->loadDataFormFile($arr);
    }

    /**
     * 从文件中加载敏感词字典
     */
    protected function loadDataFormFile($arr)
    {
        //此处可以修改为读文件,一般敏感词为文件形式,一行对应一个敏感词
        //如果经常调用的话,还可以通过缓存处理(redis、memcache)等等,此处不详细处理
        //将敏感词加入此次节点
        foreach ($arr as $value) {
            $this->addWords(trim($value));
        }
    }
    /**
     * 分割文本
     * @param $str
     * @return array[]|false|string[]
     */
    protected function splitStr($str)
    {
        //将字符串分割成组成它的字符
        // 其中/u 表示按unicode(utf-8)匹配(主要针对多字节比如汉字),否则默认按照ascii码容易出现乱码
        return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
    }

    /**
     * 添加敏感字至节点
     * @param $words
     */
    protected function addWords($words)
    {
        //1.分割字典
        $wordArr = $this->splitStr($words);
        $curNode = &$this->dict;
        foreach ($wordArr as $char) {
            if (!isset($curNode)) {
                $curNode[$char] = [];
            }
            $curNode = &$curNode[$char];
        }
        //标记到达当前节点完整路径为"敏感词"
        $curNode['end']++;
    }

    /**
     * 敏感词校验
     * @param $str ;需要校验的字符串
     * @param int $level ;屏蔽词校验等级 1-只要顺序包含都屏蔽;2-中间间隔skipDistance个字符就屏蔽;3-全词匹配即屏蔽
     * @param int $skipDistance ;允许敏感词跳过的最大距离,如笨aa蛋a傻瓜等等
     * @param bool $isReplace ;是否需要替换,不需要的话,返回是否有敏感词,否则返回被替换的字符串
     * @param string $replace ;替换字符
     * @return bool|string
     */
    public function filter($str, $level = 1, $skipDistance = 2, $isReplace = true, $replace = '*')
    {
        //允许跳过的最大距离
        if ($level == 1) {
            $maxDistance = strlen($str) + 1;
        } elseif ($level == 2) {
            $maxDistance = max($skipDistance, 0) + 1;
        } else {
            $maxDistance = 2;
        }
        $strArr = $this->splitStr($str);
        $strLength = count($strArr);
        $isSensitive = false;
        for ($i = 0; $i < $strLength; $i++) {
            //判断当前敏感字是否有存在对应节点
            $curChar = $strArr[$i];
            if (!isset($this->dict[$curChar])) {
                continue;
            }
            $isSensitive = true; //引用匹配到的敏感词节点
            $curNode = &$this->dict[$curChar];
            $dist = 0;
            $matchIndex = [$i]; //匹配后续字符串是否match剩余敏感词
            for ($j = $i + 1; $j < $strLength && $dist < $maxDistance; $j++) {
                if (!isset($curNode[$strArr[$j]])) {
                    $dist++; continue;
                }
                //如果匹配到的话,则把对应的字符所在位置存储起来,便于后续敏感词替换
                $matchIndex[] = $j;
                //继续引用
                $curNode = &$curNode[$strArr[$j]];
            }
            //判断是否已经到敏感词字典结尾,是的话,进行敏感词替换
            if (isset($curNode['end']) && $isReplace) {
                foreach ($matchIndex as $index) {
                    $strArr[$index] = $replace;
                }
                $i = max($matchIndex);
            }
        }
        if ($isReplace) {
            return implode('', $strArr);
        } else {
            return $isSensitive;
        }
    }
}

使用

require_once "../../model/filterWords.php";

$str = '测试评论:艾迪在KTV出 111 售防卫刀具专卖';
$data = json_decode(file_get_contents(Spider_PATH."../sensitive"),true);
$filterWords = new filterWords($data);
$str = $filterWords->filter($str,1);
print_r($str);
posted @ 2021-11-11 14:13  上路抗压  阅读(444)  评论(0编辑  收藏  举报