不能用 strings.replaceall 处理用户上传文本,因其将全文加载内存、字节级替换导致 utf-8 多字节字符(如中文、emoji)被截断乱码,且每词全量扫描引发高 gc 压力;必须用 []rune 按字符处理,并配合 ac 自动机实现高效、安全的敏感词过滤。

为什么不能用 strings.ReplaceAll 处理用户上传文本
直接调用 strings.ReplaceAll 会把整段文本加载进内存,对 UTF-8 多字节字符(中文、emoji、生僻字)做字节级替换,结果是“王八蛋”被切成三段乱码,匹配永远失败;更糟的是,每加一个敏感词就扫一遍全文,500 个词 = 500 次全量扫描,GC 压力飙升,线上一压就超时。
必须用 []rune 而不是 []byte 处理文本
Go 的 []byte 遍历的是字节索引,不是字符。一个中文字符占 3 字节,“王”在 UTF-8 下是 0xe7\0x8e\0x8b,for i := range text 会取到 0、1、2 三个非法位置,建树和匹配全部错位。
- 插入敏感词时:用
for _, r := range []rune(word),别用for i := range word - 扫描用户文本时:先转
chars := []rune(text),再for i, r := range chars - 纯 ASCII 日志可优化?必须先校验:
if !utf8.ValidString(text) { return err },否则中文一来就漏词
生产环境必须用 AC 自动机,别自己写 Trie
自己实现前缀树容易踩坑:node.children 是 nil map 却直接赋值、isEnd 在中间节点就设为 true 导致“王”字就触发拦截、“ababa”里只匹配到 “abab” 漏掉 “baba”。
- 用
github.com/BobuSumisu/ahocorasick—— 它支持 Unicode、可增量构建、不 panic,比已归档的golang.org/x/exp/ahocorasick更稳 - 关键词含括号、点号等特殊字符?先
regexp.QuoteMeta(word)转义,否则 AC 机解析逻辑会崩 - 热更新词库?别在 HTTP 请求里调
trie.Build(),改用trie.AddWord()+ 后台异步Build(),用sync.RWMutex保护全局指针替换
Gin 中间件里做流式过滤,别在 handler 里拼字符串
用户上传文本可能达几 MB,一次性读进内存再处理,容易 OOM;且 Gin 默认不提供流式 body 读取,需手动控制 Reader。
- 用
c.Request.Body做分块读取:buf := make([]byte, 4096),循环io.ReadFull(c.Request.Body, buf),每次喂给 AC 机匹配 - 替换逻辑别用
strings.ReplaceAllFunc—— 它不支持重叠匹配,且内部仍做全量拷贝;正确做法是调trie.FindAllStringIndex(text),按起始位置倒序排序后,用strings.Builder一次性构造结果 - HTTP 接口热重载?加简单鉴权:
if c.GetHeader("X-Admin-Token") != expectedToken { c.AbortWithStatus(403); return }
AC 自动机的初始化成本藏在第一次 Build() 里,但后续匹配是 O(n),真正卡住你的往往不是算法,而是 rune 转换漏校验、map 访问没判 nil、热更新时锁没用对 —— 这些地方一错,压测时才暴露,本地永远测不出来。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











