我需要处理大量文本,其中一个步骤是删除所有非字母数字字符。我试图找到一种有效的方法来做到这一点。
到目前为止,我有两个功能:
func stripMap(str, chr string) string {
return strings.Map(func(r rune) rune {
if strings.IndexRune(chr, r) < 0 {
return r
}
return -1
}, str)
}
Run Code Online (Sandbox Code Playgroud)
在这里,我实际上必须输入一个包含所有非字母字符的字符串。
和普通的旧正则表达式
func stripRegex(in string) string {
reg, _ := regexp.Compile("[^a-zA-Z0-9 ]+")
return reg.ReplaceAllString(in, "")
}
Run Code Online (Sandbox Code Playgroud)
正则表达式似乎要慢得多
BenchmarkStripMap-8 30000 37907 ns/op 8192 B/op 2 allocs/op
BenchmarkStripRegex-8 10000 131449 ns/op 57552 B/op 35 allocs/op
Run Code Online (Sandbox Code Playgroud)
寻找建议。还有其他更好的方法吗?改善以上?
因为幸存的符文少于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()
}
Run Code Online (Sandbox Code Playgroud)
此函数的一个变体是通过调用 result.Grow 来预分配结果:
func strip(s string) string {
var result strings.Builder
result.Grow(len(s))
...
Run Code Online (Sandbox Code Playgroud)
这确保该函数进行一次内存分配,但如果幸存的符文与源符文的比率较低,则该内存分配可能会显着大于所需的内存。
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]
}
Run Code Online (Sandbox Code Playgroud)
根据使用的实际数据,此答案中的方法之一可能比问题中的方法更快。
从大文本中删除所有非字母数字字符的有效方法。
在 Go 中,“高效方式”意味着我们运行 Gotesting包基准测试。
您对大文本的描述含糊不清。我们假设它以来自文件或其他byte切片的文本开始。
您可能会产生string([]byte)、 、make([]byte)和 的开销string([]byte)。
您可以使用来减少和 几个 的strings.Builder开销。string([]byte)make([]byte)
string([]byte)您可以通过从函数开始进一步减少这一点clean([]byte) string。
例如,
func clean(s []byte) string {
j := 0
for _, b := range s {
if ('a' <= b && b <= 'z') ||
('A' <= b && b <= 'Z') ||
('0' <= b && b <= '9') ||
b == ' ' {
s[j] = b
j++
}
}
return string(s[:j])
}
Run Code Online (Sandbox Code Playgroud)
对于大型文本,莎士比亚全集作为[]byte,
$ go fmt && go test strip_test.go -bench=. -benchmem
BenchmarkSendeckyMap-8 20 65988121 ns/op 11730958 B/op 2 allocs/op
BenchmarkSendeckyRegex-8 5 242834302 ns/op 40013144 B/op 130 allocs/op
BenchmarkThunder-8 100 21791532 ns/op 34682926 B/op 43 allocs/op
BenchmarkPeterSO-8 100 16172591 ns/op 5283840 B/op 1 allocs/op
$
Run Code Online (Sandbox Code Playgroud)
strip_test.go:
package main
import (
"io/ioutil"
"regexp"
"strings"
"testing"
)
func stripMap(str, chr string) string {
return strings.Map(func(r rune) rune {
if strings.IndexRune(chr, r) >= 0 {
return r
}
return -1
}, str)
}
var alphanum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "
func BenchmarkSendeckyMap(b *testing.B) {
for N := 0; N < b.N; N++ {
b.StopTimer()
bytShakespeare := []byte(strShakespeare)
b.StartTimer()
strShakespeare = string(bytShakespeare)
stripMap(strShakespeare, alphanum)
}
}
func stripRegex(in string) string {
reg, _ := regexp.Compile("[^a-zA-Z0-9 ]+")
return reg.ReplaceAllString(in, "")
}
func BenchmarkSendeckyRegex(b *testing.B) {
for N := 0; N < b.N; N++ {
b.StopTimer()
bytShakespeare := []byte(strShakespeare)
b.StartTimer()
strShakespeare = string(bytShakespeare)
stripRegex(strShakespeare)
}
}
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()
}
func BenchmarkThunder(b *testing.B) {
for N := 0; N < b.N; N++ {
b.StopTimer()
bytShakespeare := []byte(strShakespeare)
b.StartTimer()
strShakespeare = string(bytShakespeare)
strip(strShakespeare)
}
}
func clean(s []byte) string {
j := 0
for _, b := range s {
if ('a' <= b && b <= 'z') ||
('A' <= b && b <= 'Z') ||
('0' <= b && b <= '9') ||
b == ' ' {
s[j] = b
j++
}
}
return string(s[:j])
}
func BenchmarkPeterSO(b *testing.B) {
for N := 0; N < b.N; N++ {
b.StopTimer()
bytShakespeare := []byte(strShakespeare)
b.StartTimer()
clean(bytShakespeare)
}
}
var strShakespeare = func() string {
// The Complete Works of William Shakespeare by William Shakespeare
// http://www.gutenberg.org/files/100/100-0.txt
data, err := ioutil.ReadFile(`/home/peter/shakespeare.100-0.txt`)
if err != nil {
panic(err)
}
return string(data)
}()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4167 次 |
| 最近记录: |