用go语言实现敏感字过滤的一个例子

246 阅读2分钟

wordfilter

用go语言实现的敏感词过滤

  • 参考文章 www.cnblogs.com/taotaozhuan… 我是按照该文章提供的思路,实现的敏感词过滤。

    • 查找过滤的算法,是重新写的,没有使用原来的算法。
    • 加载词库的算法,是从文件中读取,没有使用原来的算法。
    • 过滤的结果,是返回一个bool值,表示是否包含敏感词。
    • 包含了vscode 的launch.json,可以按F5 调试运行
  • 代码例子 zdhsoft/wordfilter: 用go语言实现的敏感词过滤 (github.com)

  • 下面是主要代码:

package wordfilter

import (
	"bufio"
	"os"
	"strings"
)

var (
	tree = wordTree{root: &trieNode{ChildMap: make(map[rune]*trieNode, 256), End: false}, replaceChar: '*'}
)

type trieNode struct {
	ChildMap map[rune]*trieNode // 本节点下的所有子节点
	Data     string             // 在最后一个节点保存完整的一个内容
	End      bool               // 标识是否最后一个节点
}

type wordTree struct {
	root        *trieNode
	replaceChar rune
}

// AddWord 添加敏感词到前缀树
func (t *wordTree) AddWord(word string) {
	r := t.root
	chars := []rune(word)
	cnt := len(chars)
	for idx := 0; idx < cnt; idx++ {
		ch := chars[idx]
		r = r.AddChild(ch)
	}
	r.End = true
	r.Data = word
}

/*
替换字符
  - chars 要替换的字符数组
  - begin 开始位置
  - end 结束位置
  - replaceChar 替换字符
*/
func (t *wordTree) replaceRune(chars []rune, begin int, end int, replaceChar rune) {
	for i := begin; i < end; i++ {
		chars[i] = replaceChar
	}
}

/*
判断是否包含敏感词
  - startIndex 开始搜索的位置
  - paramText 要搜索的文本
  - paramReplace 是否替换敏感词
  - paramReplaceChar 替换字符
  - return 返回是否找到敏感词,找到的敏感词的结束位置
*/
func (t *wordTree) FindWord(startIndex int, paramText []rune, paramReplace bool, paramReplaceChar rune) (bool, int) {
	found := false
	endIndex := -1
	for range [1]bool{} {
		r := t.root
		if r == nil {
			break
		}
		cnt := len(paramText)

		if startIndex < 0 || startIndex >= cnt {
			break
		}

		foundStart := -1
		i := startIndex

		for ; i < cnt; i++ {
			ch := paramText[i]
			next := r.FindChild(ch)
			if next == nil {
				// 如果没有下一个节点
				if foundStart == -1 {
					// 如果没有匹配到,则下一个字
					continue
				} else {
					// 如果有开始,如果当前节点是脏字, 则表示找到
					if r.End {
						found = true
						endIndex = i
						break
					} else {
						i = foundStart + 1
						r = t.root
						foundStart = -1
						continue
					}
				}
			} else {
				if foundStart == -1 {
					foundStart = i
				}
				r = next
			}
		}

		if i == cnt && foundStart != -1 {
			if r.End {
				found = true
				endIndex = i
			}
		}

		if found && paramReplace {
			t.replaceRune(paramText, foundStart, endIndex, paramReplaceChar)
		}
	}
	return found, endIndex
}

// AddChild 前缀树添加字节点
func (n *trieNode) AddChild(ch rune) *trieNode {
	if n.ChildMap == nil {
		n.ChildMap = make(map[rune]*trieNode, 10)
	}
	child, ok := n.ChildMap[ch]
	if !ok {
		child = &trieNode{ChildMap: nil, End: false}
		n.ChildMap[ch] = child
		return n.ChildMap[ch]
	} else {
		return child
	}
}

// FindChild 前缀树寻找字节点
func (n *trieNode) FindChild(c rune) *trieNode {
	if n.ChildMap == nil {
		return nil
	}

	if trieNode, ok := n.ChildMap[c]; ok {
		return trieNode
	}
	return nil
}

// LoadWordFile 加载敏感词文件
func loadWordFile(paramWordFile string) (int, error) {
	var retErr error = nil
	var retCode int = 0

	for range [1]int{} {
		f, err := os.Open(paramWordFile)
		if err != nil {
			retErr = err
			retCode = 1
			break
		}
		defer f.Close()
		scanner := bufio.NewScanner(f)
		for scanner.Scan() {
			tree.AddWord(strings.TrimSpace(scanner.Text()))
		}

		if err := scanner.Err(); err != nil {
			retErr = err
			retCode = 1
			break
		}
	}
	return retCode, retErr
}

// 初始化敏感词树
func Init(wordFile string) (int, error) {
	retCode, retErr := loadWordFile(wordFile)
	return retCode, retErr
}

// 检查字符串中,是否包含敏感词
func HasSensitiveWord(paramText string) bool {
	cnt := len(paramText)
	if cnt == 0 {
		return false
	}
	textChars := []rune(paramText)
	found, _ := tree.FindWord(0, textChars, false, '*')
	return found
}

// 过滤敏感词,并用*替换
func FilterSensitiveWord(paramText string) string {
	result := ""
	cnt := len(paramText)
	if cnt == 0 {
		return paramText
	}
	textChars := []rune(paramText)

	found := true
	endIndex := 0

	FoundCnt := 0

	for found {
		found, endIndex = tree.FindWord(endIndex, textChars, true, '*')
		if !found {
			break
		} else {
			FoundCnt++
		}

	}

	if FoundCnt > 0 {
		result = string(textChars)
	} else {
		result = paramText
	}
	return result
}