如何将 []int8 转换为字符串

How to convert []int8 to string

[]int8 转换为字符串的最佳方式(性能最快)是什么?

对于 []byte 我们可以做 string(byteslice),但是对于 []int8 它给出了一个错误:

cannot convert ba (type []int8) to type string

我从 *sqlx.RowsSliceScan() 方法得到了 ba,它产生 []int8 而不是 string

这个解决方案是最快的吗?

func B2S(bs []int8) string {
    ba := []byte{}
    for _, b := range bs {
        ba = append(ba, byte(b))
    }
    return string(ba)
}

编辑我的错,它是uint8而不是int8..所以我可以直接string(ba)

Convert between slices of different types”可以肯定的是,您必须从原始 int8[].

构建正确的切片

我最终使用了 runeint32 别名)(playground), assuming that the uint8 were all simple ascii character. That is obviously an over-simplification and icza's 有更多相关信息。
另外 SliceScan() 方法最终返回 uint8[]

package main

import (
    "fmt"
)

func main() {
    s := []int8{'a', 'b', 'c'}
    b := make([]rune, len(s))
    for i, v := range s {
        b[i] = rune(v)
    }
    fmt.Println(string(b))
}

但我没有针对使用 []byte.

进行基准测试

不完全确定它是最快的,但我还没有发现更好的东西。 将 ba := []byte{} 更改为 ba := make([]byte,0, len(bs) 所以最后你有:

func B2S(bs []int8) string {
    ba := make([]byte,0, len(bs))
    for _, b := range bs {
        ba = append(ba, byte(b))
    }
    return string(ba)
}

这样追加函数将永远不会尝试插入更多数据以适合切片的底层数组,您将避免不必要地复制到更大的数组。

事先注意: 提问者首先声明输入切片是 []int8 所以这就是答案的目的。后来他意识到输入是 []uint8 可以直接转换为 string 因为 byteuint8 的别名(而 []byte => string 转换受 language spec) 支持。


您无法转换不同类型的切片,您必须手动进行。

问题是我们应该转换成什么类型​​的切片?我们有 2 个候选人:[]byte[]rune。字符串在内部存储为 UTF-8 编码的字节序列([]byte),一个 string 也可以转换为一段符文。该语言支持将这两种类型([]byte[]rune)转换为 string.

A rune 是一个 unicode 代码点。如果我们尝试以一对一的方式将 int8 转换为 rune,如果输入包含编码为多个字节的字符(使用 UTF -8) 因为在这种情况下多个 int8 值应该在一个 rune.

中结束

让我们从字符串"世界"开始,它的字节是:

fmt.Println([]byte("世界"))
// Output: [228 184 150 231 149 140]

及其符文:

fmt.Println([]rune("世界"))
// [19990 30028]

只有2个符文和6个字节。所以显然 1 对 1 int8->rune 映射不起作用,我们必须使用 1-1 int8->byte 映射。

byteuint8 的别名,范围为 0..255,要将其转换为 []int8(范围为 -128..127),我们如果字节值大于 127,则必须使用 -256+bytevalue,因此 []int8 中的 "世界" string 如下所示:

[-28 -72 -106 -25 -107 -116]

我们想要的向后转换是:bytevalue = 256 + int8value 如果 int8 是负数但我们不能像 int8 (范围 -128。 .127) 而不是 byte(范围 0..255),因此我们还必须先将其转换为 int(最后返回 byte)。这可能看起来像这样:

if v < 0 {
    b[i] = byte(256 + int(v))
} else {
    b[i] = byte(v)
}

但实际上,由于有符号整数是使用 2's complement 表示的,如果我们简单地使用 byte(v) 转换,我们会得到相同的结果(在负数的情况下,这是等效的到 256 + v).

注意: 因为我们知道切片的长度,所以分配这个长度的切片并使用索引设置它的元素要快得多 [] 和不调用内置 append 函数。

所以这是最终的转换:

func B2S(bs []int8) string {
    b := make([]byte, len(bs))
    for i, v := range bs {
        b[i] = byte(v)
    }
    return string(b)
}

Go Playground 上试试。

使用不安全的包。

func B2S(bs []int8) string {
  return strings.TrimRight(string(*(*[]byte)unsafe.Pointer(&bs)), "\x00")
}

再发一次^^