[]byte解码成[]rune真的很容易(简单地转换成string,然后转换成[]rune效果很好,我假设它默认为utf8,并带有无效字符的填充字节).我的问题是——你打算如何将[]rune解码回utf8格式的[]byte

我是不是遗漏了什么,或者我的[]rune中的每个符文都需要手动呼叫EncodeRune?当然有一个编码器,我可以简单地把Writer传给它.

推荐答案

您可以简单地将符文切片([]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上try ):

Hello 世界
Hello 世界

Go Specification: Conversions人明确提到了这个 case :Conversions to and from a string type,第3点:

将符号片转换为字符串类型会生成一个字符串,该字符串是转换为字符串的各个符号值的串联.

请注意,上面的解决方案-尽管可能是最简单的-可能不是最有效的.原因是它首先创建一个string值,该值将保存UTF-8编码形式的符文的"副本",然后它将字符串的支持片复制到结果字节片(必须进行复制,因为string值是不可变的,并且如果结果片将与string共享数据,我们将能够修改string的内容;有关详细信息,请参见golang: []byte(string) vs []byte(*string)Immutable string and pointer address).

请注意,智能编译器可以检测到无法引用中间string值,从而删除其中一个副本.

我们可以通过分配单字节片并将符文逐个编码到其中来获得更好的性能.我们就完事了.要轻松做到这一点,我们可以调用unicode/utf8一揽子计划来帮助我们:

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))

以上的输出是相同的.在Go Playground号公路上试试吧.

请注意,为了创建结果切片,我们必须猜测结果切片将有多大.我们使用最大估计值,即符文数量乘以符文可以编码到的最大字节数(utf8.UTFMax).在大多数情况下,这将比需要的更大.

我们可以创建第三个版本,首先计算所需的确切尺寸.为此,我们可以使用utf8.RuneLen()函数.这样做的好处是,我们不会"浪费"内存,也不必进行最后的切片(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

正如怀疑的那样,第二个版本速度更快,第三个版本速度最快,尽管性能yield 不是很大.通常情况下,首选第一个最简单的解决方案,但如果这是在应用程序的某个关键部分(并且执行了很多次),那么使用第三个版本可能是值得的.

Go相关问答推荐

T的Golang通用切片,其中 *T实现接口

在保留额外参数的同时解封YAML

理解Golang并发:缓冲通道的意外行为

如何使用GO GIN从Auth0 JWT内标识检索权限

Makefilego version和read命令

无法使用exec从管道中读取.Go中的命令

Go 中的 protobuf FieldMask 解组

使用 Golang 获取目录磁盘使用情况

仅使用公共 api 对 alexedwards/scs 进行简单测试

正确的 shell 程序进入 golang alpine docker 容器的入口点?

Opensearch 错误 ping 弹性服务器:由未知权威签署的 x509 证书

Golang 网络应用程序安全性:您是否应该判断输入是否为有效的 utf-8?

如何根据 Go 中第二次出现的分隔符拆分字符串?

如何使用 Status 字段创建 Kubernetes 对象?

同时调用应该只获取一次数据

如何正确为 Go 中的值设置多种类型?

Grafana/Prometheus 将多个 ip 可视化为查询

Golang API 的 HTTPS

(如何)我可以基于接口抽象地实现Stringer吗?

Golang 查询扫描未将查询正确扫描到 struct 中