所以很容易将[]byte
解码为[]rune
(简单地转换为string
,然后转换为[]rune
效果非常好,我假设默认值to utf8和forvalids的填充字节)。我的问题是 - 您如何以utf8格式将此[]rune
解码回[]byte
?
我错过了某些内容,还是为[]rune
中的每一个符文手动调用了EncodeRune?当然有一个编码器,我可以简单地将Writer
传递给。
答案 0 :(得分:30)
您只需将符文切片([]rune
)转换为string
即可转换回[]byte
。
示例:
rs := []rune{'H', 'e', 'l', 'l', 'o', ' ', '世', '界'}
bs := []byte(string(rs))
fmt.Printf("%s\n", bs)
fmt.Println(string(bs))
输出(在Go Playground上尝试):
Hello 世界
Hello 世界
Go Specification: Conversions明确提及此案例:Conversions to and from a string type,第3点:
将一片符文转换为字符串类型会产生一个字符串,该字符串是转换为字符串的各个符文值的串联。
请注意,上述解决方案 - 虽然可能是最简单的 - 可能不是最有效的。原因是它首先创建一个string
值,该值将以UTF-8编码形式保存符文的“副本”,然后将字符串的后备切片复制到结果字节切片(副本具有由于string
值是不可变的,并且如果结果切片与string
共享数据,我们将能够修改string
的内容;有关详细信息,请参阅{ {3}}和golang: []byte(string) vs []byte(*string))。
请注意,智能编译器可以检测到无法引用中间string
值,从而消除其中一个副本。
我们可以通过分配单个字节切片来获得更好的性能,并将符文逐个编码到其中。我们已经完成了。为了方便起见,我们可以致电Immutable string and pointer address包来帮助我们:
rs := []rune{'H', 'e', 'l', 'l', 'o', ' ', '世', '界'}
bs := make([]byte, len(rs)*utf8.UTFMax)
count := 0
for _, r := range rs {
count += utf8.EncodeRune(bs[count:], r)
}
bs = bs[:count]
fmt.Printf("%s\n", bs)
fmt.Println(string(bs))
以上的输出是一样的。在unicode/utf8
上尝试。
请注意,为了创建结果切片,我们必须猜测结果切片的大小。我们使用了最大估计,即符文数乘以符文可编码的最大字节数(utf8.UTFMax
)。在大多数情况下,这将比需要的更大。
我们可能会创建第三个版本,我们首先计算所需的确切大小。为此,我们可以使用Go Playground函数。获得的好处是我们不会“浪费”记忆,我们也不必进行最后的切片(bs = bs[:count]
)。
让我们比较一下表演。要比较的3个功能(3个版本):
func runesToUTF8(rs []rune) []byte {
return []byte(string(rs))
}
func runesToUTF8Manual(rs []rune) []byte {
bs := make([]byte, len(rs)*utf8.UTFMax)
count := 0
for _, r := range rs {
count += utf8.EncodeRune(bs[count:], r)
}
return bs[:count]
}
func runesToUTF8Manual2(rs []rune) []byte {
size := 0
for _, r := range rs {
size += utf8.RuneLen(r)
}
bs := make([]byte, size)
count := 0
for _, r := range rs {
count += utf8.EncodeRune(bs[count:], r)
}
return bs
}
基准代码:
var rs = []rune{'H', 'e', 'l', 'l', 'o', ' ', '世', '界'}
func BenchmarkFirst(b *testing.B) {
for i := 0; i < b.N; i++ {
runesToUTF8(rs)
}
}
func BenchmarkSecond(b *testing.B) {
for i := 0; i < b.N; i++ {
runesToUTF8Manual(rs)
}
}
func BenchmarkThird(b *testing.B) {
for i := 0; i < b.N; i++ {
runesToUTF8Manual2(rs)
}
}
结果:
BenchmarkFirst-4 20000000 95.8 ns/op
BenchmarkSecond-4 20000000 84.4 ns/op
BenchmarkThird-4 20000000 81.2 ns/op
正如所怀疑的那样,第二个版本更快,第三个版本最快,但性能提升并不大。一般来说,首选最简单的解决方案是首选,但如果这是您应用程序的某些关键部分(并且执行了很多次),则第三个版本可能值得使用。