Golang實(shí)現(xiàn)DFA算法對(duì)敏感詞過(guò)濾功能
什么是DFA算法
DFA全稱(chēng):Deterministic Finite Automaton,翻譯過(guò)來(lái)就是確定性有限自動(dòng)機(jī),其特征是,有一個(gè)有限狀態(tài)集合和一些從一個(gè)狀態(tài)通向另一個(gè)狀態(tài)的邊,每條邊上標(biāo)記有一個(gè)符號(hào),其中一個(gè)狀態(tài)是初態(tài),某些狀態(tài)是終態(tài),但是確定性有窮自動(dòng)機(jī)不會(huì)從同一狀態(tài)觸發(fā)的兩個(gè)邊標(biāo)志由相同的符號(hào)。
通俗的講DFA算法就是把你要匹配的做成一顆字典樹(shù),然后對(duì)你輸入的內(nèi)容進(jìn)行匹配的過(guò)程
如何構(gòu)建這顆字典樹(shù)呢
這是一顆簡(jiǎn)單字典樹(shù)的,我們的第一步就是構(gòu)建出一個(gè)這樣的包含敏感詞的樹(shù)
下面我說(shuō)一下構(gòu)建過(guò)程
每個(gè)節(jié)點(diǎn)的結(jié)構(gòu)
// 定義一個(gè)Node結(jié)構(gòu)體,代表DFA的一個(gè)節(jié)點(diǎn)。 type Node struct { End bool // End字段表示是否為一個(gè)單詞的結(jié)束。 Next map[rune]*Node // Next字段是一個(gè)映射,用于存儲(chǔ)此節(jié)點(diǎn)的所有子節(jié)點(diǎn)。 }
// 定義一個(gè)DFAMatcher結(jié)構(gòu)體,代表一個(gè)完整的DFA。 type DFAMatcher struct { replaceChar rune // replaceChar字段是替換敏感詞的字符。 root *Node // root字段是DFA的根節(jié)點(diǎn)。 }
我們要先創(chuàng)捷出一個(gè)root節(jié)點(diǎn),在root節(jié)點(diǎn)中是不存放數(shù)據(jù)的
//創(chuàng)建出一個(gè)DFA樹(shù)的根節(jié)點(diǎn)實(shí)例 func NewDFAMather() *DFAMatcher { return &DFAMatcher{ root: &Node{ End: false, }, } }
在確定完節(jié)點(diǎn)的結(jié)構(gòu)后,我們需要跟據(jù)敏感詞來(lái)構(gòu)建這顆字典樹(shù)
// Build方法用于構(gòu)建DFA,它會(huì)將提供的所有單詞添加到DFA中。 func (d *DFAMatcher) Build(words []string) { for _, item := range words { // 遍歷提供的所有單詞。 d.root.AddWord(item) // 將每一個(gè)單詞添加到DFA的根節(jié)點(diǎn)。 } } // AddWord方法用于向當(dāng)前節(jié)點(diǎn)添加一個(gè)單詞。 // 這個(gè)方法會(huì)遍歷單詞的每一個(gè)字符,并為每一個(gè)字符添加一個(gè)子節(jié)點(diǎn)。 func (n *Node) AddWord(word string) { node := n // 從當(dāng)前節(jié)點(diǎn)開(kāi)始。 chars := []rune(word) // 將字符串轉(zhuǎn)化為rune類(lèi)型的切片,以便處理Unicode字符。 for index, _ := range chars { // 遍歷單詞的每一個(gè)字符。 node = node.AddChild(chars[index]) // 遞歸地為每一個(gè)字符添加子節(jié)點(diǎn)。 } node.End = true // 設(shè)置最后一個(gè)節(jié)點(diǎn)為單詞的結(jié)束。 } // AddChild方法向當(dāng)前節(jié)點(diǎn)添加一個(gè)子節(jié)點(diǎn)。 // 如果子節(jié)點(diǎn)已經(jīng)存在,它將不會(huì)被重復(fù)添加。 func (n *Node) AddChild(c rune) *Node { if n.Next == nil { // 如果Next字段為nil,則初始化一個(gè)映射。 n.Next = make(map[rune]*Node) } //檢查字符c是否已經(jīng)是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)。 if next, ok := n.Next[c]; ok { // 如果ok為true,則字符c已經(jīng)是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn),直接返回該子節(jié)點(diǎn)。 return next } else { // 否則,創(chuàng)建一個(gè)新的節(jié)點(diǎn),并將其設(shè)置為當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)。 n.Next[c] = &Node{ End: false, Next: nil, } return n.Next[c] // 返回新創(chuàng)建的子節(jié)點(diǎn)。 } }
根據(jù)上面的代碼就可一構(gòu)建出一顆包含你傳入的敏感詞的樹(shù),在這顆樹(shù)種根節(jié)點(diǎn)不存放數(shù)據(jù)
過(guò)濾關(guān)鍵詞
下面就是跟據(jù)你傳入的內(nèi)容來(lái)過(guò)濾敏感詞了,你可以把敏感詞替換成其他字符,也可以統(tǒng)計(jì)敏感詞的個(gè)數(shù),這就看你自己需要什么了
下面是代碼實(shí)現(xiàn)
// Match方法用于在文本中查找并替換敏感詞。 // 它返回找到的敏感詞列表和替換后的文本。 func (d *DFAMatcher) Match(text string) (sensitiveWords []string, replaceText string) { if d.root == nil { // 如果DFA是空的,直接返回原始文本。 return nil, text } textChars := []rune(text) // 將文本轉(zhuǎn)化為rune類(lèi)型的切片,以便處理Unicode字符。 textCharsCopy := make([]rune, len(textChars)) // 創(chuàng)建一個(gè)文本字符的副本,用于替換敏感詞。 copy(textCharsCopy, textChars) // 復(fù)制原始文本字符到副本。 length := len(textChars) // 獲取文本的長(zhǎng)度。 for i := 0; i < length; i++ { // 遍歷文本的每一個(gè)字符。 // 在DFA樹(shù)中查找當(dāng)前字符對(duì)應(yīng)的子節(jié)點(diǎn) temp := d.root.FindChild(textChars[i]) if temp == nil { continue // 如果不存在匹配,繼續(xù)檢查下一個(gè)字符 } j := i + 1 // 遍歷文本中的字符,查找匹配的敏感詞,第一個(gè)匹配上了,就進(jìn)行后面的向下匹配 for ; j < length && temp != nil; j++ { if temp.End { // 如果找到一個(gè)敏感詞,將其添加到結(jié)果列表中,并在副本中替換為指定字符 sensitiveWords = append(sensitiveWords, string(textChars[i:j])) replaceRune(textCharsCopy, '*', i, j) //替換敏感詞 } temp = temp.FindChild(textChars[j]) } // 處理文本末尾的情況,如果末尾是一個(gè)完整的敏感詞,添加到結(jié)果列表中,并在副本中替換為指定字符 if j == length && temp != nil && temp.End { sensitiveWords = append(sensitiveWords, string(textChars[i:length])) replaceRune(textCharsCopy, '*', i, length) } } return sensitiveWords, string(textCharsCopy) // 返回匹配到的敏感詞列表和替換后的文本 } // FindChild方法用于在當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)中查找一個(gè)特定的子節(jié)點(diǎn)。 func (n *Node) FindChild(c rune) *Node { if n.Next == nil { // 如果Next字段為nil,則直接返回nil。 return nil } //檢查字符c是否是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)。 if _, ok := n.Next[c]; ok { // 如果ok為true,則字符c是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn),返回該子節(jié)點(diǎn)。 return n.Next[c] } return nil // 否則,返回nil。 } //替換掉文章中出現(xiàn)的關(guān)鍵詞 func replaceRune(chars []rune, replaceChar rune, begin int, end int) { for i := begin; i < end; i++ { chars[i] = replaceChar } }
以上就是使用Golang代碼實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的DFA算法過(guò)濾敏感詞的一個(gè)算法,這個(gè)算法相對(duì)于其他的性能更好,匹配更快。
到此這篇關(guān)于Golang實(shí)現(xiàn)DFA算法對(duì)敏感詞過(guò)濾功能的文章就介紹到這了,更多相關(guān)Golang敏感詞過(guò)濾內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang實(shí)現(xiàn)自己的Redis(有序集合跳表)實(shí)例探究
這篇文章主要為大家介紹了Golang實(shí)現(xiàn)自己的Redis(有序集合跳表)實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Go語(yǔ)言使用defer+recover解決panic導(dǎo)致程序崩潰的問(wèn)題
如果協(xié)程出現(xiàn)了panic,就會(huì)造成程序的崩潰,這時(shí)可以在goroutine中使用recover來(lái)捕獲panic,進(jìn)行處理,本文就詳細(xì)的介紹一下,感興趣的可以了解一下2021-09-09golang讀取yaml配置文件的方法實(shí)現(xiàn)
本文主要介紹了golang讀取yaml配置文件的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-10-10Go語(yǔ)言Elasticsearch數(shù)據(jù)清理工具思路詳解
這篇文章主要介紹了Go語(yǔ)言Elasticsearch數(shù)據(jù)清理工具思路詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10Golang實(shí)現(xiàn)拓?fù)渑判?DFS算法版)
這篇文章主要介紹了Golang實(shí)現(xiàn)拓?fù)渑判?DFS算法版),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11