我需要处理大量文本,其中一个步骤是删除所有非字母数字字符。我正试图找到一种有效的方法来做到这一点。
到目前为止我有两个功能:
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 ]
中,则该字节是要删除的 rune 的一部分。
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))
...
这确保函数进行一次内存分配,但如果幸存 rune 与源 rune 的比率很低,则内存分配可能会比需要的大得多。
此答案中的 strip
函数被编写为与 string
参数和结果类型一起使用,因为这些是问题中使用的类型。
如果应用程序正在处理 []byte
源文本并且可以修改该源文本,那么就地更新 []byte
会更有效.为此,将幸存的字节复制到 slice 的开头并在完成后重新 slice 。这避免了 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]
}
根据使用的实际数据,此答案中的一种方法可能比问题中的方法更快。
关于go - 从大文本中删除所有非字母数字字符的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54461423/