Golang如何过滤文本中的不良词汇

Golang如何过滤文本中的不良词汇 你好

我编写了过滤不良词汇的代码,希望你能在聊天项目或项目评论页面中使用。这个代码非常快速且轻量级,请告诉我你对代码的评价或批评,感谢阅读。

GitHub图标

GitHub

头像

Overover1400/FilterBadWords

FilterBadWords - 过滤所有词汇

此致


更多关于Golang如何过滤文本中的不良词汇的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

是的,我会在你的帮助下更新那个库,但目前我没有时间。

感谢回复

更多关于Golang如何过滤文本中的不良词汇的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


目前它是一个 main 包。您是否计划将其改造成可在聊天或论坛程序中引用的库包?

非常感谢您的回复。感谢约翰·斯图亚特的鼓励、改进以及出色的工程技术。我会根据改进来执行您的建议。

此致 敬礼

你好 Hamed,

我阅读了你的代码,很喜欢。你有一个很棒的程序,我想鼓励你改进它并将其公开为一个可供人们使用的 Go 库。

以下是一些反馈,即你可以添加的改进建议。

可用性:

  • 在 README 中添加关于程序限制的信息,例如:“我在一个包含 1,000,000 个字符的文本上测试了此程序,其中 10% 是敏感词,程序在 2.3 秒内完成”
  • 提供一种方式,能够导入和使用多个敏感词文件,支持多种格式(txt、csv、json、xls)
  • 让用户可以选择使用哪些敏感词文件,即将方法 FilterBadWords(content string) string 改为 FilterBadWords(content string, badWordsFiles …string) string
  • 让你的程序将数字和重复字符视为字母处理,例如,如果 “aien” 是敏感词,那么 “ai3n” 和 “aaien” 也应被视为敏感词

工程方面:

  • 目前你的程序仅在单个处理器上运行。如果将工作拆分到多个处理器,运行时间会快得多。因此,请使用 goroutine 来实现这一点。
  • 在最终产品中移除 Println(how long did it take)
  • 选择一个更具提示性的包名,并添加包注释(在 package main 之上的注释),解释其功能和使用场景
  • FilterBadWords 函数上方添加更详细的注释,解释你打算使用的算法
  • 你并未使用这个 mkSliceMap 做任何事情
  • 对于大型输入文件,将结果写入文件,不要将其作为字符串返回
  • 对于大型输入文件,你获取输入字符串,移除所有非 a-zA-Z 字符,然后将所有内容转换为小写,并将其存储在 joinString 变量的内存中——这会消耗大量内存
  • 每次找到敏感词时,你都会在 content 字符串中进行替换——这也会消耗大量内存

对于最后两点改进,尝试找到一个在内存方面更优、并且能在多个处理器上运行的更好算法。例如,如果你愿意分配足够的内存来存储最终答案(如果输入是 1,000,000 个字符,那么 joinString 将具有相似的大小)——更好的做法是遍历输入直到遇到空格字符,这样你就找到了下一个单词,然后将这个单词(或者如果是敏感词则写入 ***)写入输出变量。

更好的做法是,使用 2 个处理器,让一个处理文本的第一部分,另一个处理第二部分。在这两个处理器完成后,将结果写入文件并返回该文件。

希望这对你有所帮助,并希望你继续完善这个程序。

以下是对你提供的 FilterBadWords 代码库的专业评价。我基于 Go 语言的最佳实践、性能和可维护性进行了分析,并附上示例代码说明。

总体评价

你的代码实现了一个简单的不良词汇过滤功能,采用字符串替换的方式处理文本。对于小型项目或轻量级应用,这种方法足够快速和直接。然而,在大型或高并发场景下,当前实现可能存在性能瓶颈和扩展性问题。

关键点分析

  1. 优点

    • 代码简洁,易于理解和使用。
    • 无外部依赖,部署轻量。
    • 适用于基本过滤需求,如聊天项目或评论页面。
  2. 改进建议

    • 性能优化:当前使用字符串替换(如 strings.ReplaceAll)在大量词汇或长文本时效率较低。建议改用更高效的数据结构,如 Trie(前缀树),以支持 O(n) 时间复杂度的扫描。
    • 并发安全:如果项目涉及并发访问,需确保过滤函数是线程安全的。当前代码未显式处理并发,可能在高并发下出现竞态条件。
    • 可配置性:硬编码不良词汇列表限制了灵活性。建议支持从外部文件或数据库加载词汇,便于动态更新。
    • 边界情况处理:当前实现可能未处理大小写敏感、部分匹配或特殊字符(如标点符号分隔的词汇)。例如,词汇 “bad” 可能被 “b ad” 绕过。

示例代码改进

以下是一个基于 Trie 的改进版本,支持高效匹配和并发安全。假设你的原始代码类似简单替换,这里展示一个增强实现:

package main

import (
	"sync"
	"unicode"
)

// TrieNode 表示 Trie 树的节点
type TrieNode struct {
	children map[rune]*TrieNode
	isEnd    bool
}

// Filter 封装过滤逻辑
type Filter struct {
	root *TrieNode
	mu   sync.RWMutex // 确保并发安全
}

// NewFilter 创建一个新的过滤器
func NewFilter() *Filter {
	return &Filter{
		root: &TrieNode{children: make(map[rune]*TrieNode)},
	}
}

// AddWords 添加不良词汇到 Trie 树
func (f *Filter) AddWords(words []string) {
	f.mu.Lock()
	defer f.mu.Unlock()
	for _, word := range words {
		node := f.root
		for _, r := range word {
			if _, exists := node.children[r]; !exists {
				node.children[r] = &TrieNode{children: make(map[rune]*TrieNode)}
			}
			node = node.children[r]
		}
		node.isEnd = true
	}
}

// FilterText 过滤文本中的不良词汇,替换为指定字符
func (f *Filter) FilterText(text string, replace rune) string {
	f.mu.RLock()
	defer f.mu.RUnlock()
	runes := []rune(text)
	for i := 0; i < len(runes); i++ {
		if node := f.root; node != nil {
			for j := i; j < len(runes); j++ {
				r := runes[j]
				if next, exists := node.children[r]; exists {
					node = next
					if node.isEnd {
						// 匹配到完整词汇,进行替换
						for k := i; k <= j; k++ {
							runes[k] = replace
						}
						i = j // 跳过已处理部分
						break
					}
				} else {
					break
				}
			}
		}
	}
	return string(runes)
}

// 示例使用
func main() {
	filter := NewFilter()
	badWords := []string{"bad", "word", "example"}
	filter.AddWords(badWords)

	text := "This is a bad example with a bad word."
	filtered := filter.FilterText(text, '*')
	println(filtered) // 输出: "This is a *** example with a *** ****."
}

总结

你的代码是一个不错的起点,但通过上述改进,可以提升性能、可扩展性和健壮性。对于生产环境,建议添加单元测试覆盖边界情况(如空输入、Unicode 字符)。如果需要进一步优化,可以考虑集成正则表达式或机器学习模型以处理更复杂场景。

回到顶部