package keyword import ( "fmt" "strings" "unicode" "unicode/utf8" ) var stars = []rune("****") // TrieNode 表示 Trie 树的节点 type TrieNode struct { children map[rune]*TrieNode isEnd bool } // Trie 表示 Trie 树 type Trie struct { root *TrieNode } // NewTrie 创建一个新的 Trie 树 func NewTrie() *Trie { return &Trie{ root: &TrieNode{ children: make(map[rune]*TrieNode), }, } } // Insert 将字符串插入到 Trie 树中 func (t *Trie) Insert(word string) { node := t.root for _, char := range word { if _, exists := node.children[char]; !exists { node.children[char] = &TrieNode{children: make(map[rune]*TrieNode)} } node = node.children[char] } node.isEnd = true } // ReverseString 反转字符串 func ReverseString(s string) string { runes := []rune(s) for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { runes[i], runes[j] = runes[j], runes[i] } return string(runes) } // 第二版写法 FilterSensitiveWords 使用 Trie 树过滤敏感词 func FilterSensitiveWords(text string, trie *Trie) string { var result []rune var lastMatchEnd int // 标准化文本 text = strings.ToLower(text) runes := []rune(text) tn := len(runes) var pch rune // 遍历文本 for i := 0; i < tn; { // 查找匹配 // fmt.Printf("\ni: %d\n", i) node := trie.root j := i ch1 := runes[j] // fmt.Printf("%c", ch1) // fmt.Printf("text[i:]: %s \n", text[i:i+w1]) for j < tn { //fmt.Printf("\nj: %d\n", j) char := runes[j] if node.children[char] == nil { break } //fmt.Printf("%c ", char) node = node.children[char] j += 1 ch2 := rune(0) samePre := false if j < tn { ch2 = runes[j] samePre = node.children[ch2] != nil } //检查是否有相同前缀的其他敏感词 if node.isEnd && !samePre { // 匹配到敏感词,替换为* if unicode.Is(unicode.Arabic, char) { //如果当前字符是阿拉伯字符则判断单词前面和单词后面和一个字符是否为阿拉伯字符 // fmt.Printf("ch: %c\n", ch) // fmt.Printf("text[j+1:]: %s\n", text[j:]) // if unicode.Is(unicode.Arabic, ch2) || unicode.Is(unicode.Arabic, pch) { //如果也是则不过滤,这个可能是一个新词 break } } result = append(result, stars...) lastMatchEnd = j break } } // 如果没有匹配到敏感词,保留原字符 if j == i { result = append(result, ch1) j += 1 } else if j != lastMatchEnd { result = append(result, runes[i:j]...) } pch = ch1 i = j } return string(result) } // 第一版写法 func FilterSensitiveWords2(text string, trie *Trie) string { var filteredText strings.Builder var lastMatchEnd int // 标准化文本 text = strings.ToLower(text) tn := len(text) var pch rune // 遍历文本 for i := 0; i < tn; { // 查找匹配 //fmt.Printf("\ni: %d\n", i) node := trie.root j := i ch1, _ := utf8.DecodeRuneInString(text[j:]) // fmt.Printf("%c", ch1) // fmt.Printf("text[i:]: %s \n", text[i:i+w1]) for j < tn { char, width := utf8.DecodeRuneInString(text[j:]) if node.children[char] == nil { break } //fmt.Printf("%c ", char) node = node.children[char] j += width if node.isEnd { // 匹配到敏感词,替换为* if unicode.Is(unicode.Arabic, char) { //如果当前字符是阿拉伯字符则判断单词前面和单词后面和一个字符是否为阿拉伯字符 ch2, wi := utf8.DecodeRuneInString(text[j:]) if j+wi < tn { // fmt.Printf("ch: %c\n", ch) // fmt.Printf("text[j+1:]: %s\n", text[j:]) // if unicode.Is(unicode.Arabic, ch2) || unicode.Is(unicode.Arabic, pch) { //如果也是则不过滤,这个可能是一个新词 // filteredText.WriteString(text[i:j]) break } } } filteredText.WriteString(strings.Repeat("*", 4)) lastMatchEnd = j break } } // 如果没有匹配到敏感词,保留原字符 if j == i { char, width := utf8.DecodeRuneInString(text[i:]) filteredText.WriteRune(char) j += width } else if j != lastMatchEnd { filteredText.WriteString(text[i:j]) } pch = ch1 i = j } return filteredText.String() } func TestMatch() { runes := []rune("he样s1") fmt.Printf("runes: %v\n", runes) LoadKeywordsLocal() //阿拉伯语序从右到左 rt := ParseKeyword("بوسهعلىالصدر") //相同前缀 // rt := keyword.ParseKeyword("بوس") // rt := ParseKeyword("22بوس36") // rt := keyword.ParseKeyword("تف") // rt := keyword.ParseKeyword("oمهبلseductive") // rt := keyword.ParseKeyword("🍎🍎🍼🍼💦💦👅👅✊🍼🍆🍆👇杨涛") // rt := ParseKeyword("مهبل你好 مهبل مهبل ee") // rt := keyword.ParseKeyword("مهبلd ee") fmt.Printf("rt: %v\n", rt) }