2 回答
TA贡献1777条经验 获得超10个赞
因为存活的符文少于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]
}
根据使用的实际数据,此答案中的一种方法可能比问题中的方法更快。
TA贡献1853条经验 获得超6个赞
从大文本中删除所有非字母数字字符的有效方法。
在 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])
}
对于大文,莎士比亚全集作为一部[]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
$
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)
}()
- 2 回答
- 0 关注
- 137 浏览
添加回答
举报