Go 语言前缀树实现敏感词检测

目录
  • 一、前言
  • 二、敏感词检测
    • 暴力匹配
    • 正则匹配
  • 三、Go 语言实现敏感词前缀树
    • 前缀树结构
    • 添加敏感词
    • 匹配敏感词
    • 过滤特殊字符
    • 添加拼音检测
  • 四、源代码

一、前言

大家都知道游戏文字、文章等一些风控场景都实现了敏感词检测,一些敏感词会被屏蔽掉或者文章无法发布。今天我就分享用Go实现敏感词前缀树来达到文本的敏感词检测,让我们一探究竟!

二、敏感词检测

实现敏感词检测都很多种方法,例如暴力、正则、前缀树等。例如一个游戏的文字交流的场景,敏感词会被和谐成 * ,该如何实现呢?首先我们先准备一些敏感词如下:

sensitiveWords := []string{
   "傻逼",
   "傻叉",
   "垃圾",
   "妈的",
   "sb",
}

由于文章审核原因敏感词就换成别的了,大家能理解意思就行。

当在游戏中输入 什么垃圾打野,傻逼一样,叫你来开龙不来,sb, 该如何检测其中的敏感词并和谐掉

暴力匹配

sensitiveWords := []string{
   "傻逼",
   "傻叉",
   "垃圾",
   "妈的",
   "sb",
}
text := "什么垃圾打野,傻逼一样,叫你来开龙不来,sb"
for _, word := range sensitiveWords {
   text = strings.Replace(text, word, "*", -1)
}
println("text -> ", text)

这样采用的Go的内置的字符串替换的方法来进行暴力替换结果如下:

text ->  什么*打野,*一样,叫你来开龙不来,*

但暴力替换的时间复杂度太高了O(N^2),不建议这样,而且和谐的字符只有一个 *,感觉像屏蔽了一个字一样,因此改造一下并引出go中的 rune 类型。

sensitiveWords := []string{
   "傻逼",
   "傻叉",
   "垃圾",
   "妈的",
   "sb",
}
text := "什么垃&圾打野,傻&逼一样,叫你来开龙不来,s&b"
for _, word := range sensitiveWords {
   replaceChar := ""
   for i, wordLen := 0, len(word); i < wordLen; i++ {
      // 根据敏感词的长度构造和谐字符
      replaceChar += "*"
   }
   text = strings.Replace(text, word, replaceChar, -1)
}
println("text -> ", text)
>>>out
text ->  什么******打野,******一样,叫你来开龙不来,**

为什么中文的和谐字符多了这么?*

因为Go中默认采用utf-8来进行中文字符编码,因此一个中文字符要占3个字节

因此引出 Go 中的 rune 类型,它可以代表一个字符编码的int32的表现形式,就是说一个字符用一个数字唯一标识。有点像 ASCII 码一样 a => 97, A => 65

源码解释如下

// rune is an alias for int32 and is equivalent to int32 in all ways. It is used, by convention, to distinguish character values from integer values.

type rune = int32

因此将敏感词字符串转换成rune类型的数组然后来计算其字符个数

sensitiveWords := []string{
   "傻逼",
   "傻叉",
   "垃圾",
   "妈的",
   "sb",
}
text := "什么垃圾打野,傻逼一样,叫你来开龙不来,sb"
for _, word := range sensitiveWords {
   replaceChar := ""
   for i, wordLen := 0, len([]rune(word)); i < wordLen; i++ {
      // 根据敏感词的长度构造和谐字符
      replaceChar += "*"
   }
   text = strings.Replace(text, word, replaceChar, -1)
}
println("text -> ", text)
>>>out
text ->  什么**打野,**一样,叫你来开龙不来,**

正则匹配

// 正则匹配
func regDemo() {
   sensitiveWords := []string{
      "傻逼",
      "傻叉",
      "垃圾",
      "妈的",
      "sb",
   }
   text := "什么垃圾打野,傻逼一样,叫你来开龙不来,sb"
   // 构造正则匹配字符
   regStr := strings.Join(sensitiveWords, "|")
   println("regStr -> ", regStr)
   wordReg := regexp.MustCompile(regStr)
   text = wordReg.ReplaceAllString(text, "*")
   println("text -> ", text)
}
>>>out
regStr ->  傻逼|傻叉|垃圾|妈的|sb
text   ->  什么*打野,*一样,叫你来开龙不来,*

再优化下:

// 正则匹配敏感词
func regDemo(sensitiveWords []string, matchContents []string) {
   banWords := make([]string, 0) // 收集匹配到的敏感词
   // 构造正则匹配字符
   regStr := strings.Join(sensitiveWords, "|")
   wordReg := regexp.MustCompile(regStr)
   println("regStr -> ", regStr)
   for _, text := range matchContents {
      textBytes := wordReg.ReplaceAllFunc([]byte(text), func(bytes []byte) []byte {
         banWords = append(banWords, string(bytes))
         textRunes := []rune(string(bytes))
         replaceBytes := make([]byte, 0)
         for i, runeLen := 0, len(textRunes); i < runeLen; i++ {
            replaceBytes = append(replaceBytes, byte('*'))
         }
         return replaceBytes
      })
      fmt.Println("srcText        -> ", text)
      fmt.Println("replaceText    -> ", string(textBytes))
      fmt.Println("sensitiveWords -> ", banWords)
   }
}
func main() {
   sensitiveWords := []string{
      "傻逼",
      "傻叉",
      "垃圾",
      "妈的",
      "sb",
   }
   matchContents := []string{
      "什么垃圾打野,傻逼一样,叫你来开龙不来,sb",
   }
   regDemo(sensitiveWords, matchContents)
}
>>>out
regStr ->  傻逼|傻叉|垃圾|妈的|sb
srcText        ->  什么垃圾打野,傻逼一样,叫你来开龙不来,sb
replaceText    ->  什么**打野,**一样,叫你来开龙不来,**
sensitiveWords ->  [垃圾 傻逼 sb]

这里是通过敏感词去构造正则表达式然后再去匹配。

本文重点是使用Go实现前缀树完成敏感词的匹配,具体细节都在这里实现。

三、Go 语言实现敏感词前缀树

前缀树结构

前缀树、也称字典树(Trie),是N叉树的一种特殊形式,前缀树的每一个节点代表一个字符串(前缀)。每一个节点会有多个子节点,通往不同子节点的路径上有着不同的字符。子节点代表的字符串是由节点本身的原始字符串,以及通往该子节点路径上所有的字符组成的。

如上图所示,就是一颗前缀树,注意前缀树的根节点不存数据。那么我们该如何表示一颗前缀树呢?

可以参考一下二叉树的节点结构

type BinTreeNode struct {
   Val        string
   LeftChild  *BinTreeNode
   RightChild *BinTreeNode
}

二叉树,一个节点最多只能有两个孩子节点,非常明确,而前缀是一颗多叉树,一个节点不确定有多少子节点,因此可以用 切片Slice、Map 来存储子节点,然后一般会设置标志位 End 来标识是否是字符串的最后一个节点。结构如下

// TrieNode 敏感词前缀树节点
type TrieNode struct {
   childMap map[rune]*TrieNode // 本节点下的所有子节点
   Data     string             // 在最后一个节点保存完整的一个内容
   End      bool               // 标识是否最后一个节点
}

这里采用 Map 来存储子节点,更方便找字节点。key是rune类型(字符),value是子节点。Data则是在最后一个节点保存完整的一个内容。

// SensitiveTrie 敏感词前缀树
type SensitiveTrie struct {
   replaceChar rune // 敏感词替换的字符
   root        *TrieNode
}

这里再用另一个结构体来代表整个敏感词前缀树。

添加敏感词

添加敏感词用于构造一颗敏感词前缀树。

相对每个节点来说 childMap 都是保存相同前缀字符的子节点

// AddChild 前缀树添加
func (tn *TrieNode) AddChild(c rune) *TrieNode {
   if tn.childMap == nil {
      tn.childMap = make(map[rune]*TrieNode)
   }
   if trieNode, ok := tn.childMap[c]; ok {
      // 存在不添加了
      return trieNode
   } else {
      // 不存在
      tn.childMap[c] = &TrieNode{
         childMap: nil,
         End:      false,
      }
      return tn.childMap[c]
   }
}

敏感词前缀树则是一个完整的敏感词的粒度来添加

// AddWord 添加敏感词
func (st *SensitiveTrie) AddWord(sensitiveWord string) {
   // 将敏感词转换成rune类型(int32)
   tireNode := st.root
   sensitiveChars := []rune(sensitiveWord)
   for _, charInt := range sensitiveChars {
      // 添加敏感词到前缀树中
      tireNode = tireNode.AddChild(charInt)
   }
   tireNode.End = true
   tireNode.Data = sensitiveWord
}

具体是把敏感词转换成 []rune 类型来代表敏感词中的一个个字符,添加完后再将最后一个字符节点的End设置True,Data为完整的敏感词数据。

可能这样还不好理解,举个例子:

// SensitiveTrie 敏感词前缀树
type SensitiveTrie struct {
   replaceChar rune // 敏感词替换的字符
   root        *TrieNode
}
// TrieNode 敏感词前缀树节点
type TrieNode struct {
   childMap map[rune]*TrieNode // 本节点下的所有子节点
   Data     string             // 在最后一个节点保存完整的一个内容
   End      bool               // 标识是否最后一个节点
}
// NewSensitiveTrie 构造敏感词前缀树实例
func NewSensitiveTrie() *SensitiveTrie {
   return &SensitiveTrie{
      replaceChar: '*',
      root:        &TrieNode{End: false},
   }
}
// AddWord 添加敏感词
func (st *SensitiveTrie) AddWord(sensitiveWord string) {
   // 将敏感词转换成utf-8编码后的rune类型(int32)
   tireNode := st.root
   sensitiveChars := []rune(sensitiveWord)
   for _, charInt := range sensitiveChars {
      // 添加敏感词到前缀树中
      tireNode = tireNode.AddChild(charInt)
   }
   tireNode.End = true
   tireNode.Data = sensitiveWord
}
// AddChild 前缀树添加子节点
func (tn *TrieNode) AddChild(c rune) *TrieNode {
   if tn.childMap == nil {
      tn.childMap = make(map[rune]*TrieNode)
   }
   if trieNode, ok := tn.childMap[c]; ok {
      // 存在不添加了
      return trieNode
   } else {
      // 不存在
      tn.childMap[c] = &TrieNode{
         childMap: nil,
         End:      false,
      }
      return tn.childMap[c]
   }
}
func main() {
    sensitiveWords := []string{
       "傻逼",
       "傻叉",
       "垃圾",
    }
    st := NewSensitiveTrie()
    for _, word := range sensitiveWords {
       fmt.Println(word, []rune(word))
       st.AddWord(word)
    }
}
>>>out
傻逼 [20667 36924]
傻叉 [20667 21449]
垃圾 [22403 22334]

添加前两个敏感词傻逼、傻叉,有一个共同的前缀 傻、rune-> 200667

  • 前缀的root是没有孩子节点,添加第一个敏感词时先转换成 []rune(可以想象成字符数组)
  • 遍历rune字符数组,先判断有没有孩子节点(一开始root是没有的),没有就先构造,然后把 傻(200667) 存到 childMap中 key 为 傻(200667),value 为 TrieNode 但没有任何数据然后返回当前新增的节点
TrieNode{
    childMap: nil
    End:      false,
}
  • 此时添加 逼(36924) ,同样做2的步骤,傻逼这个敏感词添加完成走出for循环,然后将End=true、Data=傻逼

  • 添加第二个敏感词傻叉的时候又是从根节点开始,此时root有childMap,也存在傻(20667)节点,则是直接不添加把傻(20667)节点返回,然后再此节点上继续添加 叉(21449),不存在添加到傻节点的childMap中。

  • 添加第三个敏感词垃 圾,又从根节点开始,垃(22403) ,根节点不存在该子节点,故添加到根节点的childMap中,然后返回新增的 垃(22403)节点
  • 在垃节点基础上添加 圾(22334) 节点,不存在子节点则添加并返回。

由此一颗敏感词前缀树就构造出来了。

总结:添加敏感词字符节点存在不添加返回存在的节点,不存在添加新字符节点并返回新添节点,当敏感词的所有字符都添加完毕后,让最后一个节点,End=true,存储一个完整的敏感词。

匹配敏感词

将待匹配的内容转换成 []rune 类型,然后遍历寻找前缀树种第一个匹对的前缀节点,然后从后一个位置继续,直到完整匹配到了敏感词,将匹配文本的敏感词替换成 *

// FindChild 前缀树寻找字节点
func (tn *TrieNode) FindChild(c rune) *TrieNode {
   if tn.childMap == nil {
      return nil
   }
   if trieNode, ok := tn.childMap[c]; ok {
      return trieNode
   }
   return nil
}
// replaceRune 字符替换
func (st *SensitiveTrie) replaceRune(chars []rune, begin int, end int) {
   for i := begin; i < end; i++ {
      chars[i] = st.replaceChar
   }
}
// Match 查找替换发现的敏感词
func (st *SensitiveTrie) Match(text string) (sensitiveWords []string, replaceText string) {
   if st.root == nil {
      return nil, text
   }
   textChars := []rune(text)
   textCharsCopy := make([]rune, len(textChars))
   copy(textCharsCopy, textChars)
   for i, textLen := 0, len(textChars); i < textLen; i++ {
      trieNode := st.root.FindChild(textChars[i])
      if trieNode == nil {
         continue
      }
      // 匹配到了敏感词的前缀,从后一个位置继续
      j := i + 1
      for ; j < textLen && trieNode != nil; j++ {
         if trieNode.End {
            // 完整匹配到了敏感词,将匹配的文本的敏感词替换成 *
            st.replaceRune(textCharsCopy, i, j)
         }
         trieNode = trieNode.FindChild(textChars[j])
      }
      // 文本尾部命中敏感词情况
      if j == textLen && trieNode != nil && trieNode.End {
         if _, ok := sensitiveMap[trieNode.Data]; !ok {
            sensitiveWords = append(sensitiveWords, trieNode.Data)
         }
         sensitiveMap[trieNode.Data] = nil
         st.replaceRune(textCharsCopy, i, textLen)
      }
   }
   if len(sensitiveWords) > 0 {
      // 有敏感词
      replaceText = string(textCharsCopy)
   } else {
      // 没有则返回原来的文本
      replaceText = text
   }
   return sensitiveWords, replaceText
}

这样需要注意的是在内容的末尾匹配到了的敏感词处理,因为j+1后,会等于textLen的从而不进入for循环从而没有处理末尾,因此需要特殊处理下末尾情况。具体测试如下

// AddWords 批量添加敏感词
func (st *SensitiveTrie) AddWords(sensitiveWords []string) {
   for _, sensitiveWord := range sensitiveWords {
      st.AddWord(sensitiveWord)
   }
}
// 前缀树匹配敏感词
func trieDemo(sensitiveWords []string, matchContents []string) {
   trie := NewSensitiveTrie()
   trie.AddWords(sensitiveWords)
   for _, srcText := range matchContents {
      matchSensitiveWords, replaceText := trie.Match(srcText)
      fmt.Println("srcText        -> ", srcText)
      fmt.Println("replaceText    -> ", replaceText)
      fmt.Println("sensitiveWords -> ", matchSensitiveWords)
      fmt.Println()
   }
   // 动态添加
   trie.AddWord("牛大大")
   content := "今天,牛大大去挑战灰大大了"
   matchSensitiveWords, replaceText := trie.Match(content)
   fmt.Println("srcText        -> ", content)
   fmt.Println("replaceText    -> ", replaceText)
   fmt.Println("sensitiveWords -> ", matchSensitiveWords)
}
func main() {
   sensitiveWords := []string{
      "傻逼",
      "傻叉",
      "垃圾",
      "妈的",
      "sb",
   }
   matchContents := []string{
      "你是一个大傻逼,大傻叉",
      "你是傻叉",
      "shabi东西",
      "他made东西",
      "什么垃圾打野,傻逼一样,叫你来开龙不来,SB",
      "正常的内容",
   }
   //fmt.Println("--------- 普通暴力匹配敏感词 ---------")
   //normalDemo(sensitiveWords, matchContents)
   //
   //fmt.Println("\n--------- 正则匹配敏感词 ---------")
   //regDemo(sensitiveWords, matchContents)
   fmt.Println("\n--------- 前缀树匹配敏感词 ---------")
   trieDemo(sensitiveWords, matchContents)
}

结果如下:

--------- 前缀树匹配敏感词 ---------
srcText        ->  你是一个大傻&逼,大傻 叉
replaceText    ->  你是一个大傻&逼,大傻 叉
sensitiveWords ->  []
srcText        ->  你是傻叉
replaceText    ->  你是傻叉
sensitiveWords ->  []
srcText        ->  shabi东西
replaceText    ->  shabi东西
sensitiveWords ->  []
srcText        ->  他made东西
replaceText    ->  他made东西
sensitiveWords ->  []
srcText        ->  什么垃 圾打野,傻 逼一样,叫你来开龙不来,傻 逼东西,S B
replaceText    ->  什么**打野,**一样,叫你来开龙不来,**
sensitiveWords ->  [垃圾 傻逼]
srcText        ->  正常的内容
replaceText    ->  正常的内容
sensitiveWords ->  []

过滤特殊字符

可以发现在敏感词内容的中间添加一些空格、字符、表情都不能正确的在前缀树中匹配到。因此我们在进行匹配的时候应该过滤一些特殊的字符,只保留汉字、数字、字母,然后全部以小写来进行匹配。

// FilterSpecialChar 过滤特殊字符
func (st *SensitiveTrie) FilterSpecialChar(text string) string {
   text = strings.ToLower(text)
   text = strings.Replace(text, " ", "", -1) // 去除空格
   // 过滤除中英文及数字以外的其他字符
   otherCharReg := regexp.MustCompile("[^\u4e00-\u9fa5a-zA-Z0-9]")
   text = otherCharReg.ReplaceAllString(text, "")
   return text
}

感觉这里去除空格是多余的步骤,正则以已经帮你排除了。

  • \u4e00-\u9fa5a 代表所有的中文
  • a-zA-Z 代表大小写字母
  • 0-9 数字
  • 连起来在最前面加上一个 ^ 就是进行一个取反

添加拼音检测

最后就是添加中文的拼音检测,让输入的拼音也能正确的匹配到,拼音检测是把我们的敏感词转换成拼音然后添加到前缀树中。

实现中文转拼音可以用别人造好的轮子

go get github.com/chain-zhang/pinyin

查看源码整体的思路就是用文件把文字的rune和拼音对应上,具体细节自行查看

测试一下

// HansCovertPinyin 中文汉字转拼音
func HansCovertPinyin(contents []string) []string {
   pinyinContents := make([]string, 0)
   for _, content := range contents {
      chineseReg := regexp.MustCompile("[\u4e00-\u9fa5]")
      if !chineseReg.Match([]byte(content)) {
         continue
      }
      // 只有中文才转
      pin := pinyin.New(content)
      pinStr, err := pin.Convert()
      println(content, "->", pinStr)
      if err == nil {
         pinyinContents = append(pinyinContents, pinStr)
      }
   }
   return pinyinContents
}
func main() {
   sensitiveWords := []string{
      "傻逼",
      "傻叉",
      "垃圾",
      "妈的",
      "sb",
   }
   // 汉字转拼音
   pinyinContents := HansCovertPinyin(sensitiveWords)
   fmt.Println(pinyinContents)
 }
 >>>out
傻逼 -> sha bi
傻叉 -> sha cha
垃圾 -> la ji
妈的 -> ma de
[sha bi sha cha la ji ma de]

然后再测试敏感词匹配的效果

// Match 查找替换发现的敏感词
func (st *SensitiveTrie) Match(text string) (sensitiveWords []string, replaceText string) {
   if st.root == nil {
      return nil, text
   }
   // 过滤特殊字符
   filteredText := st.FilterSpecialChar(text)
   sensitiveMap := make(map[string]*struct{}) // 利用map把相同的敏感词去重
   textChars := []rune(filteredText)
   textCharsCopy := make([]rune, len(textChars))
   copy(textCharsCopy, textChars)
   for i, textLen := 0, len(textChars); i < textLen; i++ {
     ...
   }
   if len(sensitiveWords) > 0 {
      // 有敏感词
      replaceText = string(textCharsCopy)
   } else {
      // 没有则返回原来的文本
      replaceText = text
   }
   return sensitiveWords, replaceText
}
// 前缀树匹配敏感词
func trieDemo(sensitiveWords []string, matchContents []string) {
   // 汉字转拼音
   pinyinContents := HansCovertPinyin(sensitiveWords)
   fmt.Println(pinyinContents)
   trie := NewSensitiveTrie()
   trie.AddWords(sensitiveWords)
   trie.AddWords(pinyinContents) // 添加拼音敏感词
   for _, srcText := range matchContents {
      matchSensitiveWords, replaceText := trie.Match(srcText)
      fmt.Println("srcText        -> ", srcText)
      fmt.Println("replaceText    -> ", replaceText)
      fmt.Println("sensitiveWords -> ", matchSensitiveWords)
      fmt.Println()
   }
   // 动态添加
   trie.AddWord("牛大大")
   content := "今天,牛大大去挑战灰大大了"
   matchSensitiveWords, replaceText := trie.Match(content)
   fmt.Println("srcText        -> ", content)
   fmt.Println("replaceText    -> ", replaceText)
   fmt.Println("sensitiveWords -> ", matchSensitiveWords)
}
func main() {
   sensitiveWords := []string{
      "傻逼",
      "傻叉",
      "垃圾",
      "妈的",
      "sb",
   }
   matchContents := []string{
      "你是一个大傻逼,大傻叉",
      "你是傻叉",
      "shabi东西",
      "他made东西",
      "什么垃 圾打野,傻逼一样,叫你来开龙不来,SB",
      "正常的内容",
   }
   fmt.Println("\n--------- 前缀树匹配敏感词 ---------")
   trieDemo(sensitiveWords, matchContents)
}

结果如下:

--------- 前缀树匹配敏感词 ---------
srcText        ->  你是一个大傻逼,大傻叉                  
replaceText    ->  你是一个大**大**                          
sensitiveWords ->  [傻逼 傻叉]                               
srcText        ->  你是傻叉                                 
replaceText    ->  你是**                                    
sensitiveWords ->  [傻叉]                                    
srcText        ->  shabi东西                                 
replaceText    ->  *****东西                                 
sensitiveWords ->  [shabi]                                   
srcText        ->  他made东西                                
replaceText    ->  他****东西                                
sensitiveWords ->  [made]                                    
srcText        ->  什么垃圾打野,傻逼一样,叫你来开龙不来,SB
replaceText    ->  什么**打野**一样叫你来开龙不来**          
sensitiveWords ->  [垃圾 傻逼 sb]                            
srcText        ->  正常的内容                               
replaceText    ->  正常的内容                               
sensitiveWords ->  []                                        
srcText        ->  今天,牛大大挑战灰大大
replaceText    ->  今天***挑战灰大大
sensitiveWords ->  [牛大大]

整体效果还是挺不错的,但是一些谐音或者全部英文句子时有空格还是不能去除空格不然可能会存在误判还是不能检测出,要想充分的进行敏感词检测,首先要有完善的敏感词库,其次就是特殊情况特殊处理,最后就是先进行敏感词匹配然后再进行自然语言处理NLP完善,训练风控模型等检测效果才更只能。

四、源代码

敏感词前缀树匹配:gitee.com/huiDBK/sens…

以上就是Go 语言前缀树实现敏感词检测的详细内容,更多关于Go前缀树敏感词检测的资料请关注我们其它相关文章!

(0)

相关推荐

  • go语言数据结构之前缀树Trie

    目录 介绍 流程 代码 初始化 插入 查找 统计以XXX开头的单词个数 删除数据 介绍 Trie树:又称为单词查找树,是一种树形结构,可以应用于统计字符串,会在搜索引擎系统中用于对文本的词频统计,下图是一个Trie树的结构,同时它也是在插入数时的一个顺序图. 流程 首先应该先创建一个结构体,里面保存的是每一个节点的信息 初始化根节点,根节点应该初始化啥?啥也不用初始化,给个空就好看上图 插入:串转字符数组:遍历数组,如果下一个节点为空,创建,则继续遍历 查找:串转字符数组,遍历如何所有字符都在树

  • golang端口占用检测的使用

    在运维开发的过程中,经常碰到这样的情况:启动某个应用前,需要先检测一下端口是否被其他的应用占用了,若占用了,能否得到占用进程的PID.后续可以根据这个PID,查找是哪个应用占据这端口,然后KILL掉.非常简单的需求,思路是: 利用 netstat 命令获取当前的相关端口号的PID,然后正则表达式过滤出相关的PID即可 netstat -ano | findstr 8099 输出如下: 以windows 系统下为例,linux系统下,修改相关参数即可.代码如下: // 传入查询的端口号 // 返回

  • go单例实现双重检测是否安全的示例代码

    目录 现状 改进 双重检验示例: 是否线程安全 关于sync.Once 关于atomic和metex 结论 今天看到项目中的kafka客户端包装结构体的获取是单例模式<br>单例的实现是老生常谈的问题了,懒汉饿汉线程安全,因为看到项目中写的还是有些问题,网上go单例实现的搜索结果比较少经测试也并不靠谱,所以在这记录下 现状 当前有的项目直接使用Mutex锁,有的就直接判断nil则创建,对于前者,每次都加锁性能差,对于后者则会出现多个实例,也就不是单例了 改进 进而想要改进一下,在这不讨论饿汉和

  • go语言检测文件是否存在的方法

    本文实例讲述了go语言检测文件是否存在的方法.分享给大家供大家参考.具体分析如下: go语言检测文件是否存在,首先创建一个FileInfo,如果不报错,再通过 IsDir()检查是否是目录 复制代码 代码如下: finfo, err := os.Stat("filename.txt") if err != nil {     // no such file or dir     return } if finfo.IsDir() {     // it's a file } else

  • Go 语言前缀树实现敏感词检测

    目录 一.前言 二.敏感词检测 暴力匹配 正则匹配 三.Go 语言实现敏感词前缀树 前缀树结构 添加敏感词 匹配敏感词 过滤特殊字符 添加拼音检测 四.源代码 一.前言 大家都知道游戏文字.文章等一些风控场景都实现了敏感词检测,一些敏感词会被屏蔽掉或者文章无法发布.今天我就分享用Go实现敏感词前缀树来达到文本的敏感词检测,让我们一探究竟! 二.敏感词检测 实现敏感词检测都很多种方法,例如暴力.正则.前缀树等.例如一个游戏的文字交流的场景,敏感词会被和谐成 * ,该如何实现呢?首先我们先准备一些敏

  • SpringBoot使用前缀树过滤敏感词的方法实例

    目录 一.前缀树 二.敏感词过滤器 总结 一.前缀树 一般设计网站的时候,会有问题发布或者是内容发布的功能,这些功能的有一个很重要的点在于如何实现敏感词过滤,要不然可能会有不良信息的发布,或者发布的内容中有夹杂可能会有恶意功能的代码片段,敏感词过滤的基本的算法是前缀树算法,前缀树也就是字典树,通过前缀树匹配可以加快敏感词匹配的速度. 前缀树又称为Trie.字典树.查找树.主要特点是:查找效率高,但内存消耗大:主要应用于字符串检索.词频统计.字符串排序等. 到底什么是前缀树?前缀树的功能是如何实现

  • JAVA使用前缀树(Tire树)实现敏感词过滤、词典搜索

    目录 简介 Trie树 code 结论 简介 有时候需要对用户输入的内容进行敏感词过滤,或者实现查找文本中出现的词典中的词,用遍历的方式进行替换或者查找效率非常低,这里提供一个基于Trie树的方式,进行关键词的查找与过滤,在词典比较大的情况下效率非常高. Trie树 Trie树,又叫前缀树,多说无益,直接看图就明白了 词典:[“猪狗”, “小狗”, “小猫”, “小猪”, “垃圾”, “狗东西”] Tire数据结构: code 树节点Node.class /** * trie tree * *

  • 基于PyQT5制作一个敏感词检测工具

    设计思路:根据敏感词库文件筛选,查看输入的文本中是否包含敏感词汇.从而过滤出相关的敏感词. 导入应用相关的模块. import os import logging import sys 导入UI界面相关的模块. from PyQt5.QtWidgets import QApplication,QWidget,QVBoxLayout,QTextEdit,QGridLayout,QLineEdit,QPushButton,QFileDialog from PyQt5.QtGui import QIc

  • SpringBoot实现过滤敏感词的示例代码

    过滤敏感词 1. 创建一个储存要过滤的敏感词的文本文件 首先创建一个文本文件储存要过滤的敏感词 在下面的工具类中我们会读取这个文本文件,这里提前给出 @PostConstruct // 这个注解表示当容器实例化这个bean(服务启动的时候)之后在调用构造器之后这个方法会自动的调用 public void init(){ try( // 读取写有"敏感词"的文件,getClass表示从程序编译之后的target/classes读配置文件,读之后是字节流 // java7语法,在这里的句子

  • vue实现检测敏感词过滤组件的多种思路

    目录 写在前面 需求分析v1 思路一:使用截流方法监听输入框的input事件 思路二:使用输入框的失焦和保存按钮的点击事件 思路三:使用mixins抽取敏感词检测方法 思路四:使用promise封装敏感词检测组件(面向需求v1) 思路五:使用插槽和mixins封装敏感词检测组件(面向需求v2) 优化与改进 写在最后 写在前面   在做商户端敏感词检测的过程中,发现了一些问题,特在此总结.本文的行文思路是编写敏感词检测代码,前两个思路未采用组件化的开发思想,后三个思路根据需求变更,采用组件化的思想

  • Python实现敏感词过滤的4种方法

    在我们生活中的一些场合经常会有一些不该出现的敏感词,我们通常会使用*去屏蔽它,例如:尼玛 -> **,一些骂人的敏感词和一些政治敏感词都不应该出现在一些公共场合中,这个时候我们就需要一定的手段去屏蔽这些敏感词.下面我来介绍一些简单版本的敏感词屏蔽的方法. (我已经尽量把脏话做成图片的形式了,要不然文章发不出去) 方法一:replace过滤 replace就是最简单的字符串替换,当一串字符串中有可能会出现的敏感词时,我们直接使用相应的replace方法用*替换出敏感词即可. 缺点: 文本和敏感词少

  • Java实现敏感词过滤实例

    敏感词.文字过滤是一个网站必不可少的功能,如何设计一个好的.高效的过滤算法是非常有必要的.前段时间我一个朋友(马上毕业,接触编程不久)要我帮他看一个文字过滤的东西,它说检索效率非常慢.我把它程序拿过来一看,整个过程如下:读取敏感词库.如果HashSet集合中,获取页面上传文字,然后进行匹配.我就想这个过程肯定是非常慢的.对于他这个没有接触的人来说我想也只能想到这个,更高级点就是正则表达式.但是非常遗憾,这两种方法都是不可行的.当然,在我意识里没有我也没有认知到那个算法可以解决问题,但是Googl

  • Java实战之敏感词过滤器

    一.导包 本文的敏感词过滤器用在SpringBoot项目中,因此,首先需要在pom.xml文件中导入如下依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.spring

随机推荐