如何在golang中使用utf8将[]符号编码为[]字节?

Her*_*ton 16 string unicode utf-8 type-conversion go

所以很容易将a解码[]byte为a []rune(简单地转换为string,然后转换为[]rune非常好的工作,我假设它默认为utf8并且使用填充字节用于invalids).我的问题是 - 你怎么想把它解码[]rune[]byteutf8形式?

我错过了什么或者我是否已经为我的每一个符文手动调用EncodeRune[]rune?当然有一个编码器,我可以简单地传递Writer给.

icz*_*cza 34

您可以简单地转换可以转换回的符文切片([]rune).string[]byte

例:

rs := []rune{'H', 'e', 'l', 'l', 'o', ' ', '?', '?'}
bs := []byte(string(rs))

fmt.Printf("%s\n", bs)
fmt.Println(string(bs))
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试):

Hello ??
Hello ??
Run Code Online (Sandbox Code Playgroud)

围棋规格:换算明确提到这种情况下:转换和从字符串类型,点#3:

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

请注意,上述解决方案 - 尽管可能是最简单的 - 可能不是最有效的.原因是它首先创建一个string值,以UTF-8编码形式保存符文的"副本",然后将字符串的后备切片复制到结果字节切片(必须复制一个副本,因为string值是不可变的,如果结果切片与之共享数据string,我们就可以修改内容string;有关详细信息,请参阅golang:[] byte(string)vs [] byte(*string)不可变字符串和指针地址).

请注意,智能编译器可以检测到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))
Run Code Online (Sandbox Code Playgroud)

以上输出是一样的.在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
}
Run Code Online (Sandbox Code Playgroud)

基准代码:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

BenchmarkFirst-4        20000000                95.8 ns/op
BenchmarkSecond-4       20000000                84.4 ns/op
BenchmarkThird-4        20000000                81.2 ns/op
Run Code Online (Sandbox Code Playgroud)

怀疑,第二个版本更快,第三个版本是最快的,虽然性能增益不是很大.一般来说,首选最简单的解决方案是首选,但如果这是您的应用程序的某些关键部分(并且执行了很多次),则第三个版本可能值得使用.

  • 啊所以我错过了一些东西,它对我来说非常有意义`string < - > [] byte`,但我没有从`[]符文 - >字符串 - > []字节跳转 (2认同)