我需要阅读 [100]byte
来传输一堆 string
数据。
由于并非所有 string
的长度都正好是 100 个字符,因此 byte array
的剩余部分用 0
填充。
如果我通过 string(byteArray[:])
将 [100]byte
转换为 string
,则尾部 0
将显示为 ^@^@
。
在 C 中,string
将在 0
处终止,那么在 Go 中将此 byte array
转换为 string
的最佳方法是什么?
^@
没有显示,但如果你在终端或类似的东西上测试它,它就会在那里。原因是 Go 在找到 0 时不会停止将字节数组转换为字符串。您的示例中的 len(string(bytes))
是 5 而不是 1。这取决于输出函数,字符串是否完整(使用零)打印与否。
string(body)
。
将数据读入字节片的方法返回读取的字节数。您应该保存该号码,然后使用它来创建您的字符串。如果 n
是读取的字节数,您的代码将如下所示:
s := string(byteArray[:n])
要转换完整的字符串,可以使用:
s := string(byteArray[:len(byteArray)])
这相当于:
s := string(byteArray[:])
如果由于某种原因您不知道 n
,您可以使用 bytes
包来查找它,假设您的输入中没有嵌入空字符。
n := bytes.Index(byteArray[:], []byte{0})
或者正如 icza 指出的那样,您可以使用以下代码:
n := bytes.IndexByte(byteArray[:], 0)
利用:
s := string(byteArray[:])
string(byteArray[:])
包含 ^@
个字符
string(byteArray)
有什么区别?为什么需要使用 [:]
复制数组?
[:]
,对于字节数组,您可以。
简单的解决方案:
str := fmt.Sprintf("%s", byteArray)
不过,我不确定它的性能如何。
str
的长度为 100。请参阅 play.golang.org/p/XWrmqCbIwkB
例如,
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))
}
天真地扫描字节数组以查找零字节可能会更快,尤其是在您的大多数字符串都很短的情况下。
[]byte{0}
,则给出错误结果。在这种情况下,FirstZero()
应该返回 0
,因此当切片结果为 ""
时,它会返回 1
,切片结果为 "\x00"
。
当您不知道数组中非零字节的确切长度时,您可以先对其进行修剪:
字符串(字节。修剪(arr,“\x00”))
bytes.Trim
需要一个切片,而不是一个数组(如果 arr 实际上是一个 [100]byte
,则需要 arr[:]
)。 b) bytes.Trim
是在这里使用的错误函数。对于像 []byte{0,0,'a','b','c',0,'d',0}
这样的输入,它将返回 "abc\x00d" 而不是 "" c) 已经有一个使用 bytes.IndexByte
的正确答案,这是找到第一个零字节的最佳方法。
仅用于性能调整。
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))
}
string
可能会产生严重影响。 string
Go 中的值被定义为不可变的,整个 Go 运行时和库都以此为基础。如果你走这条路,你会将自己传送到最神秘的错误和运行时错误的中间。
用这个:
bytes.NewBuffer(byteArray).String()
byteArray[:]
,因为bytes.NewBuffer
需要一个[]byte
; b)问题说数组有你不处理的尾随零; c) 如果您的变量是 []byte
(您的行将编译的唯一方式),那么您的行只是执行 string(v)
的慢速方式。
虽然不是非常高效,但唯一可读的解决方案是:
// 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)
}
这分配了一片字节片。因此,如果大量或重复使用它,请注意性能。
使用切片而不是数组进行读取。例如, 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
s := a[:n]
或 s := string(a[:n])
对字节数组进行切片。如果 n
不是直接可用的,则必须对其进行计算,例如,如 Daniel 建议的那样,通过在缓冲区(数组)中查找特定/零字节。
这是一个删除空字节的选项:
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")
}
https://pkg.go.dev/golang.org/x/sys/unix#ByteSliceToString
https://pkg.go.dev/golang.org/x/sys/windows#ByteSliceToString
build constraints exclude all Go files in...
bytes.IndexByte()
来搜索单个byte
而不是bytes.Index()
,其字节切片包含 1 个字节。