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 關注
- 186 瀏覽
添加回答
舉報