wordMatch.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package keyword
  2. import (
  3. "fmt"
  4. "strings"
  5. "unicode"
  6. "unicode/utf8"
  7. )
  8. var stars = []rune("****")
  9. // TrieNode 表示 Trie 树的节点
  10. type TrieNode struct {
  11. children map[rune]*TrieNode
  12. isEnd bool
  13. }
  14. // Trie 表示 Trie 树
  15. type Trie struct {
  16. root *TrieNode
  17. }
  18. // NewTrie 创建一个新的 Trie 树
  19. func NewTrie() *Trie {
  20. return &Trie{
  21. root: &TrieNode{
  22. children: make(map[rune]*TrieNode),
  23. },
  24. }
  25. }
  26. // Insert 将字符串插入到 Trie 树中
  27. func (t *Trie) Insert(word string) {
  28. node := t.root
  29. for _, char := range word {
  30. if _, exists := node.children[char]; !exists {
  31. node.children[char] = &TrieNode{children: make(map[rune]*TrieNode)}
  32. }
  33. node = node.children[char]
  34. }
  35. node.isEnd = true
  36. }
  37. // ReverseString 反转字符串
  38. func ReverseString(s string) string {
  39. runes := []rune(s)
  40. for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
  41. runes[i], runes[j] = runes[j], runes[i]
  42. }
  43. return string(runes)
  44. }
  45. // 第二版写法 FilterSensitiveWords 使用 Trie 树过滤敏感词
  46. func FilterSensitiveWords(text string, trie *Trie) string {
  47. var result []rune
  48. var lastMatchEnd int
  49. // 标准化文本
  50. text = strings.ToLower(text)
  51. runes := []rune(text)
  52. tn := len(runes)
  53. var pch rune
  54. // 遍历文本
  55. for i := 0; i < tn; {
  56. // 查找匹配
  57. // fmt.Printf("\ni: %d\n", i)
  58. node := trie.root
  59. j := i
  60. ch1 := runes[j]
  61. // fmt.Printf("%c", ch1)
  62. // fmt.Printf("text[i:]: %s \n", text[i:i+w1])
  63. for j < tn {
  64. //fmt.Printf("\nj: %d\n", j)
  65. char := runes[j]
  66. if node.children[char] == nil {
  67. break
  68. }
  69. //fmt.Printf("%c ", char)
  70. node = node.children[char]
  71. j += 1
  72. ch2 := rune(0)
  73. samePre := false
  74. if j < tn {
  75. ch2 = runes[j]
  76. samePre = node.children[ch2] != nil
  77. }
  78. //检查是否有相同前缀的其他敏感词
  79. if node.isEnd && !samePre {
  80. // 匹配到敏感词,替换为*
  81. if unicode.Is(unicode.Arabic, char) {
  82. //如果当前字符是阿拉伯字符则判断单词前面和单词后面和一个字符是否为阿拉伯字符
  83. // fmt.Printf("ch: %c\n", ch)
  84. // fmt.Printf("text[j+1:]: %s\n", text[j:])
  85. //
  86. if unicode.Is(unicode.Arabic, ch2) || unicode.Is(unicode.Arabic, pch) {
  87. //如果也是则不过滤,这个可能是一个新词
  88. break
  89. }
  90. }
  91. result = append(result, stars...)
  92. lastMatchEnd = j
  93. break
  94. }
  95. }
  96. // 如果没有匹配到敏感词,保留原字符
  97. if j == i {
  98. result = append(result, ch1)
  99. j += 1
  100. } else if j != lastMatchEnd {
  101. result = append(result, runes[i:j]...)
  102. }
  103. pch = ch1
  104. i = j
  105. }
  106. return string(result)
  107. }
  108. // 第一版写法
  109. func FilterSensitiveWords2(text string, trie *Trie) string {
  110. var filteredText strings.Builder
  111. var lastMatchEnd int
  112. // 标准化文本
  113. text = strings.ToLower(text)
  114. tn := len(text)
  115. var pch rune
  116. // 遍历文本
  117. for i := 0; i < tn; {
  118. // 查找匹配
  119. //fmt.Printf("\ni: %d\n", i)
  120. node := trie.root
  121. j := i
  122. ch1, _ := utf8.DecodeRuneInString(text[j:])
  123. // fmt.Printf("%c", ch1)
  124. // fmt.Printf("text[i:]: %s \n", text[i:i+w1])
  125. for j < tn {
  126. char, width := utf8.DecodeRuneInString(text[j:])
  127. if node.children[char] == nil {
  128. break
  129. }
  130. //fmt.Printf("%c ", char)
  131. node = node.children[char]
  132. j += width
  133. if node.isEnd {
  134. // 匹配到敏感词,替换为*
  135. if unicode.Is(unicode.Arabic, char) {
  136. //如果当前字符是阿拉伯字符则判断单词前面和单词后面和一个字符是否为阿拉伯字符
  137. ch2, wi := utf8.DecodeRuneInString(text[j:])
  138. if j+wi < tn {
  139. // fmt.Printf("ch: %c\n", ch)
  140. // fmt.Printf("text[j+1:]: %s\n", text[j:])
  141. //
  142. if unicode.Is(unicode.Arabic, ch2) || unicode.Is(unicode.Arabic, pch) {
  143. //如果也是则不过滤,这个可能是一个新词
  144. // filteredText.WriteString(text[i:j])
  145. break
  146. }
  147. }
  148. }
  149. filteredText.WriteString(strings.Repeat("*", 4))
  150. lastMatchEnd = j
  151. break
  152. }
  153. }
  154. // 如果没有匹配到敏感词,保留原字符
  155. if j == i {
  156. char, width := utf8.DecodeRuneInString(text[i:])
  157. filteredText.WriteRune(char)
  158. j += width
  159. } else if j != lastMatchEnd {
  160. filteredText.WriteString(text[i:j])
  161. }
  162. pch = ch1
  163. i = j
  164. }
  165. return filteredText.String()
  166. }
  167. func TestMatch() {
  168. runes := []rune("he样s1")
  169. fmt.Printf("runes: %v\n", runes)
  170. LoadKeywordsLocal()
  171. //阿拉伯语序从右到左
  172. rt := ParseKeyword("بوسهعلىالصدر")
  173. //相同前缀
  174. // rt := keyword.ParseKeyword("بوس")
  175. // rt := ParseKeyword("22بوس36")
  176. // rt := keyword.ParseKeyword("تف")
  177. // rt := keyword.ParseKeyword("oمهبلseductive")
  178. // rt := keyword.ParseKeyword("🍎🍎🍼🍼💦💦👅👅✊🍼🍆🍆👇杨涛")
  179. // rt := ParseKeyword("مهبل你好 مهبل مهبل ee")
  180. // rt := keyword.ParseKeyword("مهبلd ee")
  181. fmt.Printf("rt: %v\n", rt)
  182. }