中文敏感词过滤需预处理干扰符、分组编译词库、DOM解析HTML;因\b对中文无效,须用字符约束和标准化确保匹配准确且不破坏语义。

用 \b 保证只匹配完整词,但中文没词边界
英文可用 \bcat\b 防止“category”被误杀,但中文没有天然词边界——\b 对“南京”“南京市”完全无效。强行套用会导致要么漏(“南京路”不触发),要么过杀(“苹果手机”里把“苹果”干掉,留下“手机”语义断裂)。
- 对纯英文/数字混合词(如“iPhone15”“Win11”),
\b仍有效,可保留 - 中文词必须靠前后字符约束:用
(? 表示“前面不是汉字、后面也不是汉字”,比 <code>\b更可控 - 但注意:标点、空格、换行、零宽字符(
\u200B)都得显式列进否定类,否则照样漏——比如“南京。”, “南京 ”, “南京\u200B”都不匹配
预处理比正则容错更可靠
用户输入的干扰手段太多:全角空格、★、*、大小写混排、零宽字符……指望正则一条规则兜底,只会越写越松、越松越误杀。
- 先统一清理:用
mb_ereg_replace('[\x{3000}\x{200B}\x{FEFF}]', ' ', $text, 'u')把常见干扰符转成半角空格 - 再标准化:转小写(
mb_strtolower($text, 'UTF-8'))、去除首尾空白、合并连续空格 - 敏感词库也按同样规则预处理,确保匹配时“输入”和“词表”在同一起跑线
- 别在正则里堆
[a-zA-Z\u4e00-\u9fa5\s\*\u2605]{0,3}这种模糊段——回溯爆炸风险高,且无法覆盖新干扰符
词组优先 + 分组编译,避免 preg_replace('/(法轮功|邪教|...)/u', '***', $text)
单条超长正则在 PHP 中编译慢、执行卡、还容易触发 PCRE 回溯限制(PREG_BACKTRACK_LIMIT_ERROR)。词一过千,preg_replace 就可能直接返回 null。
- 用
array_chunk($keywords, 200)把词表切块,每组单独编译成一个preg_replace调用 - 每组内按词长度倒序排列(长词优先),防止“法轮”先被替,漏掉“法轮功”
- 编译结果缓存到
static $cache = []或WeakMap,避免重复编译 - 不要用
.*?匹配干扰符——改用(?:[\s\u3000\*\u2605]){0,2}显式限定次数,防回溯
HTML 内容必须用 DOM 解析,不能直接正则扫字符串
preg_replace 对 HTML 是盲操作:它会把 <img src="fanlun.jpg"> 里的 “fanlun” 当敏感词干掉,导致图片加载失败;也会误杀 <script>var text = "法轮功";</script> 里的字符串,破坏 JS 逻辑。
立即学习“PHP免费学习笔记(深入)”;
- 先用
$dom = new DOMDocument(); @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);解析 - 遍历所有
TEXT_NODE,对节点nodeValue单独调用敏感词过滤函数 - 跳过
src、href、data-等属性值,以及script、style标签内容 - 最后用
$dom->saveHTML()输出,结构和编码全保



















