我似乎总是一遍又一遍地将字符串转换为[] byte到string。这有很多开销吗?还有更好的方法吗?
例如,这是一个接受UTF8字符串,对其进行规范化,删除重音,然后将特殊字符转换为ASCII等效字符的函数:
var transliterations = map[rune]string{'Æ':"AE",'Ð':"D",'Ł':"L",'Ø':"OE",'Þ':"Th",'ß':"ss",'æ':"ae",'ð':"d",'ł':"l",'ø':"oe",'þ':"th",'Œ':"OE",'œ':"oe"}
func RemoveAccents(s string) string {
b := make([]byte, len(s))
t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
_, _, e := t.Transform(b, []byte(s), true)
if e != nil { panic(e) }
r := string(b)
var f bytes.Buffer
for _, c := range r {
temp := rune(c)
if val, ok := transliterations[temp]; ok {
f.WriteString(val)
} else {
f.WriteRune(temp)
}
}
return f.String()
}
所以我从一个字符串开始,因为这就是我得到的,然后我将它转换为字节数组,然后再转换为字符串,然后再转换为字节数组,然后再转换回字符串。当然这是不必要的,但我无法弄清楚如何不这样做..?它是否真的有很多开销,或者我不必担心过度转换会减慢速度?
(如果有人有时间我还没知道bytes.Buffer
实际上是如何工作的,那么初始化字符串大小的2倍的缓冲区是不是更好,这是最大输出大小返回值?)
答案 0 :(得分:4)
在Go中,string
是不可变的,因此任何更改都会创建一个新字符串。作为一般规则,请将string
转换为byte
或rune
切片,然后转换回string
一次。为了避免重新分配,对于小型和瞬态分配,如果您不知道确切的数字,则过度分配以提供安全边际。
例如,
package main
import (
"bytes"
"fmt"
"unicode"
"unicode/utf8"
"code.google.com/p/go.text/transform"
"code.google.com/p/go.text/unicode/norm"
)
var isMn = func(r rune) bool {
return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
}
var transliterations = map[rune]string{
'Æ': "AE", 'Ð': "D", 'Ł': "L", 'Ø': "OE", 'Þ': "Th",
'ß': "ss", 'æ': "ae", 'ð': "d", 'ł': "l", 'ø': "oe",
'þ': "th", 'Œ': "OE", 'œ': "oe",
}
func RemoveAccents(b []byte) ([]byte, error) {
mnBuf := make([]byte, len(b)*125/100)
t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
n, _, err := t.Transform(mnBuf, b, true)
if err != nil {
return nil, err
}
mnBuf = mnBuf[:n]
tlBuf := bytes.NewBuffer(make([]byte, 0, len(mnBuf)*125/100))
for i, w := 0, 0; i < len(mnBuf); i += w {
r, width := utf8.DecodeRune(mnBuf[i:])
if s, ok := transliterations[r]; ok {
tlBuf.WriteString(s)
} else {
tlBuf.WriteRune(r)
}
w = width
}
return tlBuf.Bytes(), nil
}
func main() {
in := "test stringß"
fmt.Println(in)
inBytes := []byte(in)
outBytes, err := RemoveAccents(inBytes)
if err != nil {
fmt.Println(err)
}
out := string(outBytes)
fmt.Println(out)
}
输出:
test stringß
test stringss
答案 1 :(得分:2)
这个问题没有答案。如果这些转换是您应用程序中的性能瓶颈,则应修复它们。如果不是:不。
您是否在实际负载下分析了应用程序,RemoveAccents
是瓶颈?没有?那么为什么要这么麻烦呢?
真的:我认为一个人可以做得更好(在垃圾更少,迭代次数更少,转换次数更少的情况下),例如通过链接一些&#34; TransliterationTransformer&#34;。但我怀疑这将是麻烦的麻烦。
答案 2 :(得分:1)
将字符串转换为字节 slice (不是数组,即different type)会产生很小的开销。即为字节切片分配空间。
字符串是它自己的类型,是对字节序列的解释。但并非每个字节序列都是有用的字符串。字符串也是immutable。如果您查看strings package,您会看到字符串将sliced很多。
在您的示例中,您可以省略第二次转换回字符串。您还可以使用一个字节切片。
与关于绩效的每个问题一样:您可能需要衡量。字节切片的分配真的是你的瓶颈吗?
您可以像这样初始化bytes.Buffer
:
f := bytes.NewBuffer(make([]byte, 0, len(s)*2))
您的大小为0,容量是字符串大小的2倍。如果你可以估计缓冲区的大小,那么这样做可能会很好。它将为您节省一些底层字节片的重新分配。