用re.sub()配合预编译正则最稳妥,因str.replace()无法处理嵌套敏感词和词边界问题,需用或负向断言、长词优先排序、re.escape转义及IGNORECASE标志。

直接用 re.sub() 配合预编译的正则模式最稳妥,但必须注意词边界、大小写和 Unicode 问题,否则会漏匹配或误伤正常文本。
为什么不能直接用 str.replace()?
敏感词常有嵌套或部分重合(比如“苹果”和“苹果手机”),str.replace() 是贪心逐次替换,顺序错就可能留漏网之鱼;更关键的是它无法识别“独立词汇”——比如把“南京”替换成 ***,但“南京市”里不该动“南京”二字。正则的 词边界和 (? 负向先行断言才能真正按“词”处理。
- “南京路” → 不该触发“南京”过滤
- “他买了个苹果。” → “苹果”要被过滤,“苹果手机”也要被过滤,且不能重复替换
- 用户输入可能含全角空格、换行、emoji,需提前 normalize 或扩展字符类
如何正确构建带词边界的正则模式?
核心是用 或更可靠的 (?,尤其当敏感词含非 ASCII 字符(如中文)时,<code> 在某些 Python 版本下对中文支持不一致,推荐显式写负向断言。
- 编译前对敏感词排序:长词优先(如先“苹果手机”,再“苹果”),避免短词截断长词匹配
- 转义所有特殊字符:
re.escape(word)必须加,否则“a.b”会被当成正则元字符 - 加上
re.IGNORECASE标志处理大小写变体,但注意中文通常不用,可单独控制 - 示例:
import re words = ["苹果", "苹果手机", "南京"] # 排序 + 转义 + 负向断言 pattern = "|".join(re.escape(w) for w in sorted(words, key=len, reverse=True)) regex = re.compile(r"(?<!w)(" + pattern + r")(?!\w)", re.IGNORECASE) text = "我爱吃苹果,也用苹果手机,在南京市。" result = regex.sub("***", text)
实际部署时容易踩哪些坑?
本地测试通过,上线后失效很常见,多数出在编码、上下文或性能上。
立即学习“Python免费学习笔记(深入)”;
- 敏感词文件用 UTF-8 BOM 编码读入,导致首词多出
ufeff,匹配失败 —— 读取时加encoding="utf-8-sig" - 用户输入含零宽空格(
u200b)、全角标点,绕过—— 可在正则中扩展为(? 覆盖常见中文区间 - 敏感词上千条时,
|拼接的大正则会变慢甚至回溯爆炸 —— 改用 Aho-Corasick 算法(如ahocorasick库)替代 - 日志或调试输出里打印被过滤文本时,忘了脱敏,造成二次泄露 —— 替换后立即丢弃原始字段,或统一走脱敏日志处理器
真正难的不是写对一行 re.sub(),而是让模式在各种输入边界下稳定不漏不滥。词边界逻辑、字符集覆盖、编译时机、替换顺序,四者缺一都会在灰度期突然暴露问题。


















