string.replace不能用于敏感词过滤,因其不识别词边界、不处理重叠、不支持变形,且性能随词库增大断崖下跌;dfa通过预处理实现o(n)匹配,需统一大小写、支持最长匹配与词边界校验。

Replace 不能当敏感词过滤用,一上生产就出事。
为什么 string.Replace 不是敏感词过滤
它只是子串替换,不识别词边界、不处理重叠、不支持模糊变形,更无法应对“嫖娼”“嫖客”共用前缀的场景。常见错误现象:"嫖娼".Replace("嫖", "*") → "*娼",漏判且污染上下文;词库加到 100 条以上,性能断崖式下跌。
DFA 是 C# 敏感词过滤的事实标准
不是因为它“高级”,而是它把匹配复杂度从 O(n×m)(n=文本长,m=词数)压到 O(n),且初始化后无回溯、无正则引擎开销。关键点:
-
Replace每个词扫一遍全文;DFA 一次遍历完成所有词匹配 - 必须预处理大小写:统一
char.ToLower()或构建时双写节点(内存翻倍) - 必须支持「最长匹配」:词库含
"色情"和"色情网站",输入"色情网站违规"应命中后者,不是前者 - 必须检查词边界:匹配到
"和谐"后,得用char.IsLetterOrDigit()判前后是否为非字母数字字符,否则"和谐社会"会被误杀
手写 DFA 的最小可行结构
两个类足矣:TrieNode 和 DfaFilter。重点不在树怎么建,而在运行时怎么推进状态:
public int MatchLength(string text, int start)
{
var node = _root;
int maxLength = 0;
for (int i = start; i maxLength)
maxLength = node.WordLength;
}
return maxLength;
}
调用时别写双重循环:
- 错:
for (int i = 0; i - 对:用
while (i 0) { /* 替换 */ i += len; } else i++; }
上线前必须验证的三件事
DFA 初始化失败不会报错,只会静默返回空结果。生产环境必须做:
- 加载词库后断言
_root.Children.Count > 0,防空字典树 - 对典型变形词测一轮:
"sex"(全角)、"嫖娼"、"和-谐"(中间符号),确认预处理逻辑生效 - 监控
MatchLength返回值分布,若大量返回 0,说明首字符没进树——大概率是大小写/全半角没归一化
词库加载和状态机构建是一次性动作,但边界判断、最长匹配、变形兼容这些细节,漏一条,线上就会漏词或误杀。











