go - 从大文本中删除所有非字母数字字符的有效方法
问题描述
我需要处理大量文本,其中一个步骤是删除所有非字母数字字符。我试图找到一种有效的方法来做到这一点。
到目前为止,我有两个功能:
func stripMap(str, chr string) string {
return strings.Map(func(r rune) rune {
if strings.IndexRune(chr, r) < 0 {
return r
}
return -1
}, str)
}
在这里,我实际上必须提供所有非字母字符的字符串。
和普通的旧正则表达式
func stripRegex(in string) string {
reg, _ := regexp.Compile("[^a-zA-Z0-9 ]+")
return reg.ReplaceAllString(in, "")
}
正则表达式似乎要慢得多
BenchmarkStripMap-8 30000 37907 ns/op 8192 B/op 2 allocs/op
BenchmarkStripRegex-8 10000 131449 ns/op 57552 B/op 35 allocs/op
寻找建议。还有其他更好的方法吗?改善以上?
解决方案
因为幸存的 rune 小于utf8.RuneSelf,所以这个问题可以通过对字节进行操作来解决。如果任何字节不在 中[^a-zA-Z0-9 ]
,则该字节是要删除的符文的一部分。
func strip(s string) string {
var result strings.Builder
for i := 0; i < len(s); i++ {
b := s[i]
if ('a' <= b && b <= 'z') ||
('A' <= b && b <= 'Z') ||
('0' <= b && b <= '9') ||
b == ' ' {
result.WriteByte(b)
}
}
return result.String()
}
此函数的一个变体是通过调用 result.Grow 预分配结果:
func strip(s string) string {
var result strings.Builder
result.Grow(len(s))
...
这可确保函数只分配一次内存,但如果幸存符文与源符文的比率较低,则内存分配可能会显着大于所需。
此答案中的strip
函数是为使用string
参数和结果类型而编写的,因为这些是问题中使用的类型。
如果应用程序正在处理[]byte
源文本并且可以修改该源文本,那么更新[]byte
就地会更有效。为此,请将幸存的字节复制到切片的开头,并在完成后重新切片。这避免了 strings.Builder 中的内存分配和开销。这种变化类似于 peterSO 对这个问题的回答。
func strip(s []byte) []byte {
n := 0
for _, b := range s {
if ('a' <= b && b <= 'z') ||
('A' <= b && b <= 'Z') ||
('0' <= b && b <= '9') ||
b == ' ' {
s[n] = b
n++
}
}
return s[:n]
}
根据使用的实际数据,此答案中的一种方法可能比问题中的方法更快。
推荐阅读
- android - API 'variant.getAssemble()' 已过时并已替换为 'variant.getAssembleProvider()'
- python - ConnectionRefusedError:[Errno 111] 在 python 中连接被拒绝
- amazon-web-services - AWS Cloudformation 数据库实例 IAM 角色
- html - 如何在 HTML 结构中嵌套 Bootstrap 4 Grid 和 BEM 类?
- arrays - 如何在 Vue.js 的输入值字段中输入对象数组?
- c - 默认 SIGINT 处理程序如何在其库定义中实现?
- kubernetes - Kubernetes资源使用时间
- android - android - 前台服务将数据保存在内存中
- machine-learning - 强化学习 - 应用 Q-learning 来安排卡车出发时间以优化包裹递送
- javascript - 用户单击时尝试设置状态时超出最大更新深度