必须用[]rune而非[]byte处理中文敏感词,因UTF-8下中文、emoji等占多字节,[]byte遍历会切碎字符导致匹配失效;isEnd须在循环结束后赋值,需支持重叠匹配,访问map前必须判nil。
直接用 go-sensitive,别自己从零写 trie;真要手写,核心就四件事:[]rune 遍历、isend 位置对、支持重叠匹配、node.children 访问前判 nil。
为什么不能用 []byte 处理中文敏感词
中文、emoji、生僻字在 Go 里是 UTF-8 编码,一个字符占多个字节。[]byte 遍历会把“王”(3 字节)切成三个非法单元,导致树建对了、查不到,或查到但位置错乱。所有字符串操作必须统一转成 []rune:
- 插入词库时:
for _, r := range []rune(word),不是for i := range word - 扫描文本时:
chars := []rune(text),再for i, r := range chars - 如果硬要用
[]byte(比如纯 ASCII 日志),必须提前校验:if !utf8.ValidString(text) { return err }
isEnd = true 写错位置会导致误杀率飙升
常见错误是在构建 Trie 节点循环里每走一步都设 node.isEnd = true,结果“王八蛋”路径上,“王”“王八”“王八蛋”全被标为结尾。用户输“王者”,系统立刻拦截——因为“王”被当成完整敏感词了。
- 正确做法:循环完所有
rune后,在最终节点才赋值node.isEnd = true - 如需支持“前缀敏感”(“王八”和“王八羔子”都算违规),必须新增字段如
isPrefix bool,不能复用isEnd - 别信“我初始化时已经建好 map 了”——子节点的子节点可能没初始化,越深越容易漏
匹配时只返回第一个词?那是没做重叠匹配
输入“王八羔子坏”,只命中“王八”就停,漏掉更长的“王八羔子”,说明匹配逻辑一碰到 isEnd == true 就 return 或 break 了。这不是生产可用的过滤。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 关键不是“找最长”,而是“每个起点都要试”:从索引 0 开始匹配,命中后记录;再从索引 1 重新进树继续找
- 推荐双指针模式:
start固定,end推进;找到匹配后,start++,重新进树 -
strings.ReplaceAllFunc不适合生产环境:它不支持重叠,且性能差一个数量级
node.children[char] panic?90% 是没判 nil
本地小数据测不出,压测时突然 panic:assignment to entry in nil map,堆栈指向 AddChild 或匹配循环里的 node.children[char]——这就是访问了未初始化的 map[rune]*Node。
- 每次读写前必须检查:
if node.children == nil { node.children = make(map[rune]*Node) } - 更稳妥的是封装方法:
func (n *Node) setChild(r rune, child *Node),内部统一判空 - 并发加载、热更新、配置变更都可能让某分支的
children仍是nil,不能依赖“一次初始化就万事大吉”
真正难的不是建树,是让树在高并发、热更新、中英文混排、emoji 穿插的线上环境里不 panic、不漏词、不误杀——每一个 []rune、每一处 nil 判定、每一次 isEnd 赋值位置,都在决定它能不能扛住真实流量。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










