使用Go语言实现简单敏感词过滤
August 17, 2023
敏感词过滤,算是一个比较常见的功能,尤其是在内容、社交类应用中更是如此。本文介绍如何使用Go语言实现简单的敏感词过滤功能。
简单敏感词过滤-ai版 #
先列出一个gpt给出来的一个简单前缀树的实现:
// 初始化敏感词切片
var sensitiveWords = []string{}
// TrieNode 表示Trie树的节点
type TrieNode struct {
children map[rune]*TrieNode
isEnd bool
Text string
}
// Trie 表示敏感词的Trie树
type Trie struct {
root *TrieNode
}
// NewTrie 创建一个新的Trie树
func NewTrie() *Trie {
return &Trie{
root: &TrieNode{
children: make(map[rune]*TrieNode),
isEnd: false,
},
}
}
// Insert 将一个敏感词插入到Trie树中
func (t *Trie) Insert(word string) {
node := t.root
for _, char := range []rune(word) {
if _, ok := node.children[char]; !ok {
node.children[char] = &TrieNode{
children: make(map[rune]*TrieNode),
isEnd: false,
}
}
node = node.children[char]
}
node.Text = word
node.isEnd = true
}
// Contains 检测文本中是否包含敏感词
func (t *Trie) Contains(text string) bool {
node := t.root
for _, char := range []rune(text) {
if _, ok := node.children[char]; !ok {
continue
}
node = node.children[char]
if node.isEnd {
return true
}
}
return false
}
这个版本的代码中,构建了一个简单的前缀树来存储敏感词,如果某个节点存储的是敏感词的最后一个字符,则isEnd值为true。这样,当我们检测到某个节点的isEnd值为true时,就说明检测到了敏感词。
如果只是为了检测到一段文本是否包含敏感词,而不需要匹配出所有的敏感词,那实际上在敏感词a包含敏感词b时,我们可以只存储单词b。
我们编写一个测试用例,测试一下上面的代码:
func TestCheckWord1(t *testing.T) {
trie := NewTrie()
for _, word := range sensitiveWords {
trie.Insert(word)
}
content := "这里是一段非法活动文本。"
search := trie.Contains(content)
assert.Equal(t, search, true)
}
测试结果如下:

测试通过。(再这样下去程序员真要失业了!)
当然,上面的代码不完善,例如:不是并发安全的、不支持删除敏感词、没有返回检测到的敏感词。我们来完善一下。
完善敏感词过滤 #
下面我们在上面的代码基础上,添加一些功能。
在上面的版本中,我们添加了读写锁来保证并发安全,并且添加了删除敏感词的功能。敏感词库的变更,是一个并不频繁的操作,而可以预见的时,敏感词库不会太大。所以,我们是否可以在敏感词库发生变更时,直接重构整个敏感词库,在重构完成后,再切换到新的敏感词库上呢?
测试代码:
上面的测试用例中,我们添加了添加、删除敏感词功能,并校验了删除敏感词的正确性,以及在有更长的敏感词时是否会无删除。 上述用例在本机测试通过。后记 #
以上,我们实现了一个简单的敏感词过滤功能。实际上,敏感词过滤还可以做得更复杂,添加更多功能,比如,检测拼音、过滤特殊字符等等。这些功能,可以在上面的代码基础上,自行扩展。但是需要考虑的是:扩展功能的同时,是否会影响性能,尤其是在检测超长文本时。