FlashText 是一个高性能的 Aho-Corasick 自动机实现,专为 Go 语言设计。使用经典的 AC 自动机算法,能够在文本中快速查找和提取大量关键词,并保证找到所有重叠匹配。
- ⚡ 性能卓越: 比正则表达式快 60倍
- 🎯 完整匹配: 找到所有重叠匹配,不遗漏任何结果
- 📦 简单易用: 清晰的 API,5 行代码即可上手
- 🔧 生产就绪: 经过完整测试,可用于敏感词过滤、内容审核等场景
- 🌏 多语言支持: 完整的 UTF-8 和中文支持
- ✅ 完整的 AC 自动机实现 - 包含 Trie 树和失败指针机制
- ✅ 🧠 自适应容量引擎 - 越用越快,智能预测内存分配
- ✅ 重叠匹配检测 - 找到所有可能的匹配,包括重叠部分
- ✅ 大小写控制 - 支持大小写敏感/不敏感模式
- ✅ 高效批量处理 - 一次添加多个关键词
- ✅ UTF-8 完整支持 - 正确处理中文、日韩文等多字节字符
| 场景 | 性能 |
|---|---|
| 1000个关键词 + 6MB文本 | ~380ms |
| vs 正则表达式 | 快60倍 |
| 时间复杂度 | O(n) 线性 |
📊 详细性能数据请参考 PERFORMANCE.md
go get github.com/the-yex/flashtext要求: Go 1.20+
package main
import (
"fmt"
"github.com/the-yex/flashtext"
)
func main() {
// 1. 创建处理器(不区分大小写)
kp := flashtext.NewKeywordProcessor(false)
// 2. 添加关键词并构建
kp.AddKeywordsFromList([]string{"golang", "python", "java"}).Build()
// 3. 提取关键词
text := "I love Golang and Python programming!"
matches := kp.ExtractKeywords(text)
// 4. 处理结果
for _, match := range matches {
fmt.Printf("找到: %s [%d:%d]\n",
match.MatchString(), match.Start(), match.End())
}
}输出:
找到: Golang [7:13]
找到: Python [18:24]
kp := flashtext.NewKeywordProcessor(true) // 区分大小写
kp.AddKeyWord("Go").Build()
kp.ExtractKeywords("I use Go and go")
// 只匹配 "Go",不匹配 "go"data := []byte("some binary data with keywords")
matches := kp.ExtractKeywordsFromBytes(data)kp := flashtext.NewKeywordProcessor(false).
AddKeyWord("apple").
AddKeyWord("banana").
AddKeywordsFromList([]string{"orange", "grape"}).
Build()- 敏感词过滤 - 内容审核、评论检测
- 文本分析 - 关键词提取、实体识别
- 数据挖掘 - 大规模文本处理
- SEO工具 - 关键词密度分析
- 日志分析 - 错误关键词检测
- 关键词数量 < 10 (正则表达式可能更简单)
- 只需要部分匹配 (可考虑简化的 Trie 实现)
| 实现 | 时间 | 内存 | 完整性 |
|---|---|---|---|
| FlashText | 383ms | 172MB | ✅ 完整 |
| 正则表达式 | 22,900ms | 1MB | ✅ 完整 |
| 简化Trie | 172ms | 133MB | ❌ 不完整 |
在文本 "hershey" 中查找 ["he", "she", "hers"]:
FlashText (AC自动机):
✓ he [0:2]
✓ hers [0:4]
✓ she [3:6] ← 重叠匹配
✓ he [4:6] ← 重叠匹配
总计: 4个
简化Trie实现:
✓ he [0:1]
✓ hers [0:3]
✓ he [4:5]
❌ 遗漏 "she"
总计: 3个
结论: FlashText 保证找到所有匹配,这在敏感词过滤等安全场景下至关重要。
📖 更多性能数据和分析,请查看 PERFORMANCE.md
本库引入了创新的 EWMA (指数加权移动平均) 算法来动态优化内存分配。
- 智能学习: 自动学习您的业务数据的关键词密度。
- 越用越快: 随着运行时间增长,内存预分配越来越精准,扩容次数趋近于 0。
- 无感运行: 全自动后台优化,无需任何配置。
实测数据 (1000次调用): 容量在前50次调用中迅速学习并稳定,后续950次调用中扩容次数为0。
| 迭代次数 | 预估容量 | 实际匹配 | 状态 |
|---|---|---|---|
| 1 | 215 | 200 | 学习中 |
| 10 | 383 | 200 | 快速适应 |
| 50+ | 423 | 200 | 完全稳定 |
👉 了解更多设计细节: ADAPTIVE_ENGINE.md
由于自适应引擎在后台运行一个轻量级统计协程,如果您的服务在运行过程中需要频繁销毁或重新构建 KeywordProcessor,请务必调用 Close() 方法以释放资源:
kp := flashtext.NewKeywordProcessor()
// ... 使用 ...
// 当不再需要该实例,或者即将创建新实例替换它时:
kp.Close() 对于作为全局单例长期运行的 KeywordProcessor,则无需时刻关注 Close。
// 参数: caseSensitive - 是否区分大小写
kp := flashtext.NewKeywordProcessor(caseSensitive bool)// 添加单个关键词
kp.AddKeyWord(keyword string) *KeywordProcessor
// 批量添加关键词
kp.AddKeywordsFromList(keywords []string) *KeywordProcessor// 必须在添加完所有关键词后调用
kp.Build()// 从字符串提取
matches := kp.ExtractKeywords(text string) []Match
// 从字节数组提取
matches := kp.ExtractKeywordsFromBytes(data []byte) []Matchtype Match struct {
match string // 匹配的文本
start int // 开始位置(字节)
end int // 结束位置(字节)
}
// 获取方法
match.MatchString() string // 匹配的文本
match.Start() int // 开始位置
match.End() int // 结束位置请参考测试文件 keywordprocessor_test.go
# 功能测试
go test -v
# 性能测试
go test -bench=. -benchmem
# 性能对比测试
go test -bench=BenchmarkComparison -benchmem -run=^$ -timeout=30m- ✅ 基础匹配测试
- ✅ 中文字符测试
- ✅ 大小写敏感/不敏感测试
- ✅ 边缘情况测试
- ✅ 重叠匹配测试
- ✅ 性能对比测试
Python 版本在连续文本中存在匹配 bug(需要分词),而本实现能正确处理连续文本:
# Python FlashText
kp.extract_keywords('ahishehersshare') # 返回 [] ❌
kp.extract_keywords('a his he hers share') # 返回 ['his', 'he', 'hers', 'share'] ✅// FlashText Go
kp.ExtractKeywords("ahishershare") // 正确匹配所有 ✅ayoyu/flashtext 使用简化的 Trie 树,速度更快但会遗漏重叠匹配。FlashText 使用完整的 AC 自动机,保证匹配完整性。
选择建议:
- 需要完整匹配 → FlashText
- 只追求速度且不关心重叠 → ayoyu/flashtext
// ✅ 好的做法:复用实例
kp := flashtext.NewKeywordProcessor(false)
kp.AddKeywordsFromList(keywords).Build()
for _, text := range texts {
matches := kp.ExtractKeywords(text)
}
// ❌ 不好的做法:每次都创建新实例
for _, text := range texts {
kp := flashtext.NewKeywordProcessor(false)
kp.AddKeywordsFromList(keywords).Build()
matches := kp.ExtractKeywords(text)
}const chunkSize = 1024 * 1024 // 1MB
for i := 0; i < len(text); i += chunkSize {
end := min(i+chunkSize, len(text))
matches := kp.ExtractKeywords(text[i:end])
// 处理 matches...
}我们遵循单一职责原则,专注于做好一件事:高效、准确的关键词匹配。
关键词替换可以很容易地在应用层实现:
matches := kp.ExtractKeywords(text)
// 用 matches 信息自行替换这种设计让库保持简洁,同时给用户最大的灵活性。
我们欢迎各种形式的贡献!
- Fork 本仓库
- 创建您的特性分支 (
git checkout -b feature/AmazingFeature) - 提交您的更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 开启一个 Pull Request
- ✅ 添加测试用例
- ✅ 保持代码风格一致
- ✅ 更新相关文档
- ✅ 确保所有测试通过
本项目采用 MIT 许可证
- 感谢 Aho-Corasick 算法的发明者
- 参考了 Python flashtext 库的设计思路
- Issues: GitHub Issues
- Discussions: GitHub Discussions
如果这个项目对您有帮助,请给我们一个 ⭐ Star!
Made with ❤️ by the FlashText team