使用Go語言實現(xiàn)敏感詞過濾功能
簡單敏感詞過濾-ai版
先列出一個gpt給出來的一個簡單前綴樹的實現(xiàn):
// 初始化敏感詞切片
var sensitiveWords = []string{}
// TrieNode 表示Trie樹的節(jié)點
type TrieNode struct {
children map[rune]*TrieNode
isEnd bool
Text string
}
// Trie 表示敏感詞的Trie樹
type Trie struct {
root *TrieNode
}
// NewTrie 創(chuàng)建一個新的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
}
這個版本的代碼中,構(gòu)建了一個簡單的前綴樹來存儲敏感詞,如果某個節(jié)點存儲的是敏感詞的最后一個字符,則isEnd值為true。這樣,當我們檢測到某個節(jié)點的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)
}
測試結(jié)果如下:

測試通過。(再這樣下去程序員真要失業(yè)了?。?/p>
當然,上面的代碼不完善,例如:不是并發(fā)安全的、不支持刪除敏感詞、沒有返回檢測到的敏感詞。我們來完善一下。
完善敏感詞過濾
下面我們在上面的代碼基礎(chǔ)上,添加一些功能。
package sensitivewordcheck
import "sync"
// TrieV1Node 表示TrieV1樹的節(jié)點
type TrieV1Node struct {
children map[rune]*TrieV1Node // 子節(jié)點
isEnd bool
Text string
Value rune
parent *TrieV1Node // 父節(jié)點
}
// TrieV1 表示敏感詞的TrieV1樹
type TrieV1 struct {
root *TrieV1Node
lock sync.RWMutex
}
// NewTrieV1 創(chuàng)建一個新的TrieV1樹
func NewTrieV1() *TrieV1 {
return &TrieV1{
root: &TrieV1Node{
children: make(map[rune]*TrieV1Node),
isEnd: false,
},
}
}
// Insert 將一個敏感詞插入到TrieV1樹中
func (t *TrieV1) Insert(word string) {
t.lock.Lock()
defer t.lock.Unlock()
node := t.root
for _, char := range []rune(word) {
if _, ok := node.children[char]; !ok {
node.children[char] = &TrieV1Node{
children: make(map[rune]*TrieV1Node),
isEnd: false,
parent: node,
Value: char,
}
}
node = node.children[char]
}
node.Text = word
node.isEnd = true
}
// Contains 檢測文本中是否包含敏感詞
func (t *TrieV1) Contains(text string) bool {
t.lock.RLock()
defer t.lock.RUnlock()
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
}
// Check 檢測文本中是否包含敏感詞,并返回第一個敏感詞
func (t *TrieV1) Check(text string) string {
t.lock.RLock()
defer t.lock.RUnlock()
node := t.root
for _, char := range text {
if _, ok := node.children[char]; !ok {
continue
}
node = node.children[char]
if node.isEnd {
return node.Text
}
}
return ""
}
// Rebuild 重新構(gòu)建敏感詞樹
func (t *TrieV1) Rebuild(words []string) {
t.lock.Lock()
defer t.lock.Unlock()
t.root = &TrieV1Node{}
for _, word := range words {
t.Insert(word)
}
}
// Delete 刪除一個敏感詞
func (t *TrieV1) Delete(word string) {
t.lock.Lock()
defer t.lock.Unlock()
node := t.root
for _, char := range []rune(word) {
if _, ok := node.children[char]; !ok {
return
}
node = node.children[char]
if node.isEnd {
node.isEnd = false
node.Text = ""
if len(node.children) > 0 { // 有子節(jié)點,不能刪除
break
}
// 遞歸刪除
t.doDel(node)
}
}
}
func (t *TrieV1) doDel(node *TrieV1Node) {
// 再次判斷是否可以刪除
if node == nil || len(node.children) > 0 {
return
}
// 從上級節(jié)點的children中刪除本節(jié)點
delete(node.parent.children, node.Value)
// 判斷上一層節(jié)點是否可以刪除
t.doDel(node.parent)
}
在上面的版本中,我們添加了讀寫鎖來保證并發(fā)安全,并且添加了刪除敏感詞的功能。
敏感詞庫的變更,是一個并不頻繁的操作,而可以預(yù)見的時,敏感詞庫不會太大。所以,我們是否可以在敏感詞庫發(fā)生變更時,直接重構(gòu)整個敏感詞庫,在重構(gòu)完成后,再切換到新的敏感詞庫上呢?
測試代碼:
package sensitivewordcheck
import (
"github.com/stretchr/testify/assert"
"testing"
)
var trieV1 *TrieV1
func init() {
trieV1 = NewTrieV1()
for _, word := range sensitiveWords {
trieV1.Insert(word)
}
}
func TestCheckWordAndDelete(t *testing.T) {
// 添加敏感詞 非法捕魚
trieV1.Insert("非法捕魚")
assert.Equal(t, trieV1.Contains("你要去非法捕魚嗎?"), true)
// 添加敏感詞 非法打獵
trieV1.Insert("非法打獵")
assert.Equal(t, trieV1.Contains("你要去非法打獵嗎?"), true)
// 刪除敏感詞 非法打獵
trieV1.Delete("非法打獵")
// 不再包含 非法打獵
assert.Equal(t, trieV1.Contains("你要去非法打獵嗎?"), false)
// 非法捕魚 不受影響
assert.Equal(t, trieV1.Contains("你要去非法捕魚嗎?"), true)
// 更長的敏感詞
trieV1.Insert("非法捕魚工具")
assert.Equal(t, trieV1.Contains("你要去買非法捕魚工具嗎?"), true)
// 刪除 非法捕魚
trieV1.Delete("非法捕魚")
assert.Equal(t, trieV1.Contains("你要去非法捕魚嗎?"), false)
// 如果有子節(jié)點,不刪除
assert.Equal(t, trieV1.Contains("你要去買非法捕魚工具嗎?"), true)
}
上面的測試用例中,我們添加了添加、刪除敏感詞功能,并校驗了刪除敏感詞的正確性,以及在有更長的敏感詞時是否會無刪除。 上述用例在本機測試通過。
后記
以上,我們實現(xiàn)了一個簡單的敏感詞過濾功能。實際上,敏感詞過濾還可以做得更復(fù)雜,添加更多功能,比如,檢測拼音、過濾特殊字符等等。這些功能,可以在上面的代碼基礎(chǔ)上,自行擴展。但是需要考慮的是:擴展功能的同時,是否會影響性能,尤其是在檢測超長文本時。
到此這篇關(guān)于使用Go語言實現(xiàn)敏感詞過濾功能的文章就介紹到這了,更多相關(guān)Go敏感詞過濾內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

