我如何将一个零结束字节数组转换为字符串?

我需要读取[100]byte来传输一堆string数据。

因为不是所有的# eyz0都恰好是100个字符长,所以byte array的其余部分用# eyz2填充。

如果我转换[100]bytestring: string(byteArray[:]),尾0s显示为^@^@s。

在C中,string将终止于0,那么在Go中将byte array转换为string的最佳方法是什么?

505550 次浏览

将数据读入字节片的方法返回已读字节数。您应该保存该数字,然后使用它来创建字符串。如果n是读取的字节数,你的代码看起来像这样:

s := string(byteArray[:n])

要转换完整的字符串,可以使用:

s := string(byteArray[:len(byteArray)])

这相当于:

s := string(byteArray[:])

如果由于某种原因您不知道n,您可以使用bytes包来查找它,假设您的输入中没有嵌入null字符。

n := bytes.Index(byteArray[:], []byte{0})

或者正如icza指出的那样,你可以使用下面的代码:

n := bytes.IndexByte(byteArray[:], 0)
  • 使用切片而不是数组进行读取。例如,io.Reader接受片,而不是数组。

  • 使用切片代替零填充。

例子:

buf := make([]byte, 100)
n, err := myReader.Read(buf)
if n == 0 && err != nil {
log.Fatal(err)
}


consume(buf[:n]) // consume() will see an exact (not padded) slice of read data

例如,

package main


import "fmt"


func CToGoString(c []byte) string {
n := -1
for i, b := range c {
if b == 0 {
break
}
n = i
}
return string(c[:n+1])
}


func main() {
c := [100]byte{'a', 'b', 'c'}
fmt.Println("C: ", len(c), c[:4])
g := CToGoString(c[:])
fmt.Println("Go:", len(g), g)
}

输出:

C:  100 [97 98 99 0]
Go: 3 abc

下面的代码正在寻找'\0',在这个问题的假设下,数组可以被认为是排序的,因为所有非'\0'在所有'\0'之前。如果数组可以在数据中包含“\0”,那么这个假设就不成立了。

使用二进制搜索找到第一个零字节的位置,然后切片。

你可以像这样找到零字节:

package main


import "fmt"


func FirstZero(b []byte) int {
min, max := 0, len(b)
for {
if min + 1 == max { return max }
mid := (min + max) / 2
if b[mid] == '\000' {
max = mid
} else {
min = mid
}
}
return len(b)
}
func main() {
b := []byte{1, 2, 3, 0, 0, 0}
fmt.Println(FirstZero(b))
}

简单地扫描字节数组寻找零字节可能会更快,特别是如果您的大多数字符串都很短。

使用:

s := string(byteArray[:])

仅用于性能调优。

package main


import (
"fmt"
"reflect"
"unsafe"
)


func BytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}


func StringToBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(&s))
}


func main() {
b := []byte{'b', 'y', 't', 'e'}
s := BytesToString(b)
fmt.Println(s)
b = StringToBytes(s)
fmt.Println(string(b))
}

用这个:

bytes.NewBuffer(byteArray).String()

简单的解决方案:

str := fmt.Sprintf("%s", byteArray)

但我不确定它的性能如何。

当你不知道数组中非nil字节的确切长度时,你可以先修剪它:

字符串(字节。削减(arr " \ x00 "))

虽然性能不是很好,但唯一可读的解决方案是:

  // Split by separator and pick the first one.
// This has all the characters till null, excluding null itself.
retByteArray := bytes.Split(byteArray[:], []byte{0}) [0]


// OR


// If you want a true C-like string, including the null character
retByteArray := bytes.SplitAfter(byteArray[:], []byte{0}) [0]

c风格字节数组的完整示例:

package main


import (
"bytes"
"fmt"
)


func main() {
var byteArray = [6]byte{97,98,0,100,0,99}


cStyleString := bytes.SplitAfter(byteArray[:], []byte{0}) [0]
fmt.Println(cStyleString)
}

一个完整的例子,有一个Go风格的字符串不包括空值:

package main


import (
"bytes"
"fmt"
)


func main() {
var byteArray = [6]byte{97, 98, 0, 100, 0, 99}


goStyleString := string(bytes.Split(byteArray[:], []byte{0}) [0])
fmt.Println(goStyleString)
}

这将分配一个字节切片的切片。因此,如果它被大量或重复使用,请注意性能。

下面是一个删除空字节的选项:

package main
import "golang.org/x/sys/windows"


func main() {
b := []byte{'M', 'a', 'r', 'c', 'h', 0}
s := windows.ByteSliceToString(b)
println(s == "March")
}