使用Go語(yǔ)言實(shí)現(xiàn)敏感詞過(guò)濾功能
簡(jiǎn)單敏感詞過(guò)濾-ai版
先列出一個(gè)gpt給出來(lái)的一個(gè)簡(jiǎn)單前綴樹(shù)的實(shí)現(xiàn):
// 初始化敏感詞切片 var sensitiveWords = []string{} // TrieNode 表示Trie樹(shù)的節(jié)點(diǎn) type TrieNode struct { children map[rune]*TrieNode isEnd bool Text string } // Trie 表示敏感詞的Trie樹(shù) type Trie struct { root *TrieNode } // NewTrie 創(chuàng)建一個(gè)新的Trie樹(shù) func NewTrie() *Trie { return &Trie{ root: &TrieNode{ children: make(map[rune]*TrieNode), isEnd: false, }, } } // Insert 將一個(gè)敏感詞插入到Trie樹(shù)中 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 檢測(cè)文本中是否包含敏感詞 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è)版本的代碼中,構(gòu)建了一個(gè)簡(jiǎn)單的前綴樹(shù)來(lái)存儲(chǔ)敏感詞,如果某個(gè)節(jié)點(diǎn)存儲(chǔ)的是敏感詞的最后一個(gè)字符,則isEnd值為true。這樣,當(dāng)我們檢測(cè)到某個(gè)節(jié)點(diǎn)的isEnd值為true時(shí),就說(shuō)明檢測(cè)到了敏感詞。
如果只是為了檢測(cè)到一段文本是否包含敏感詞,而不需要匹配出所有的敏感詞,那實(shí)際上在敏感詞a包含敏感詞b時(shí),我們可以只存儲(chǔ)單詞b。
我們編寫(xiě)一個(gè)測(cè)試用例,測(cè)試一下上面的代碼:
func TestCheckWord1(t *testing.T) { trie := NewTrie() for _, word := range sensitiveWords { trie.Insert(word) } content := "這里是一段非法活動(dòng)文本。" search := trie.Contains(content) assert.Equal(t, search, true) }
測(cè)試結(jié)果如下:
測(cè)試通過(guò)。(再這樣下去程序員真要失業(yè)了?。?/p>
當(dāng)然,上面的代碼不完善,例如:不是并發(fā)安全的、不支持刪除敏感詞、沒(méi)有返回檢測(cè)到的敏感詞。我們來(lái)完善一下。
完善敏感詞過(guò)濾
下面我們?cè)谏厦娴拇a基礎(chǔ)上,添加一些功能。
package sensitivewordcheck import "sync" // TrieV1Node 表示TrieV1樹(shù)的節(jié)點(diǎn) type TrieV1Node struct { children map[rune]*TrieV1Node // 子節(jié)點(diǎn) isEnd bool Text string Value rune parent *TrieV1Node // 父節(jié)點(diǎn) } // TrieV1 表示敏感詞的TrieV1樹(shù) type TrieV1 struct { root *TrieV1Node lock sync.RWMutex } // NewTrieV1 創(chuàng)建一個(gè)新的TrieV1樹(shù) func NewTrieV1() *TrieV1 { return &TrieV1{ root: &TrieV1Node{ children: make(map[rune]*TrieV1Node), isEnd: false, }, } } // Insert 將一個(gè)敏感詞插入到TrieV1樹(shù)中 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 檢測(cè)文本中是否包含敏感詞 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 檢測(cè)文本中是否包含敏感詞,并返回第一個(gè)敏感詞 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)建敏感詞樹(shù) func (t *TrieV1) Rebuild(words []string) { t.lock.Lock() defer t.lock.Unlock() t.root = &TrieV1Node{} for _, word := range words { t.Insert(word) } } // Delete 刪除一個(gè)敏感詞 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é)點(diǎn),不能刪除 break } // 遞歸刪除 t.doDel(node) } } } func (t *TrieV1) doDel(node *TrieV1Node) { // 再次判斷是否可以刪除 if node == nil || len(node.children) > 0 { return } // 從上級(jí)節(jié)點(diǎn)的children中刪除本節(jié)點(diǎn) delete(node.parent.children, node.Value) // 判斷上一層節(jié)點(diǎn)是否可以刪除 t.doDel(node.parent) }
在上面的版本中,我們添加了讀寫(xiě)鎖來(lái)保證并發(fā)安全,并且添加了刪除敏感詞的功能。
敏感詞庫(kù)的變更,是一個(gè)并不頻繁的操作,而可以預(yù)見(jiàn)的時(shí),敏感詞庫(kù)不會(huì)太大。所以,我們是否可以在敏感詞庫(kù)發(fā)生變更時(shí),直接重構(gòu)整個(gè)敏感詞庫(kù),在重構(gòu)完成后,再切換到新的敏感詞庫(kù)上呢?
測(cè)試代碼:
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) { // 添加敏感詞 非法捕魚(yú) trieV1.Insert("非法捕魚(yú)") assert.Equal(t, trieV1.Contains("你要去非法捕魚(yú)嗎?"), true) // 添加敏感詞 非法打獵 trieV1.Insert("非法打獵") assert.Equal(t, trieV1.Contains("你要去非法打獵嗎?"), true) // 刪除敏感詞 非法打獵 trieV1.Delete("非法打獵") // 不再包含 非法打獵 assert.Equal(t, trieV1.Contains("你要去非法打獵嗎?"), false) // 非法捕魚(yú) 不受影響 assert.Equal(t, trieV1.Contains("你要去非法捕魚(yú)嗎?"), true) // 更長(zhǎng)的敏感詞 trieV1.Insert("非法捕魚(yú)工具") assert.Equal(t, trieV1.Contains("你要去買非法捕魚(yú)工具嗎?"), true) // 刪除 非法捕魚(yú) trieV1.Delete("非法捕魚(yú)") assert.Equal(t, trieV1.Contains("你要去非法捕魚(yú)嗎?"), false) // 如果有子節(jié)點(diǎn),不刪除 assert.Equal(t, trieV1.Contains("你要去買非法捕魚(yú)工具嗎?"), true) }
上面的測(cè)試用例中,我們添加了添加、刪除敏感詞功能,并校驗(yàn)了刪除敏感詞的正確性,以及在有更長(zhǎng)的敏感詞時(shí)是否會(huì)無(wú)刪除。 上述用例在本機(jī)測(cè)試通過(guò)。
后記
以上,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的敏感詞過(guò)濾功能。實(shí)際上,敏感詞過(guò)濾還可以做得更復(fù)雜,添加更多功能,比如,檢測(cè)拼音、過(guò)濾特殊字符等等。這些功能,可以在上面的代碼基礎(chǔ)上,自行擴(kuò)展。但是需要考慮的是:擴(kuò)展功能的同時(shí),是否會(huì)影響性能,尤其是在檢測(cè)超長(zhǎng)文本時(shí)。
到此這篇關(guān)于使用Go語(yǔ)言實(shí)現(xiàn)敏感詞過(guò)濾功能的文章就介紹到這了,更多相關(guān)Go敏感詞過(guò)濾內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Golong實(shí)現(xiàn)JWT身份驗(yàn)證的詳細(xì)過(guò)程
JWT提供了一種強(qiáng)大而靈活的方法來(lái)處理Web應(yīng)用程序中的身份驗(yàn)證和授權(quán),本教程將引導(dǎo)您逐步實(shí)現(xiàn)Go應(yīng)用程序中的JWT身份驗(yàn)證過(guò)程,感興趣的朋友跟隨小編一起看看吧2024-03-03RabbitMQ延時(shí)消息隊(duì)列在golang中的使用詳解
延時(shí)隊(duì)列常使用在某些業(yè)務(wù)場(chǎng)景,使用延時(shí)隊(duì)列可以簡(jiǎn)化系統(tǒng)的設(shè)計(jì)和開(kāi)發(fā)、提高系統(tǒng)的可靠性和可用性、提高系統(tǒng)的性能,下面我們就來(lái)看看如何在golang中使用RabbitMQ的延時(shí)消息隊(duì)列吧2023-11-11