c-如何将字节缓冲区中以null结尾的字符串转换为Go中的字符串



这:

label := string([]byte{97, 98, 99, 0, 0, 0, 0})
fmt.Printf("%sn", label)

这样做(^@是空字节):

go run test.go 
abc^@^@^@

Go的syscall包中隐藏了这个函数,它可以找到第一个空字节([]字节{0})并返回长度。我假设它被称为C长度的紧咬。

对不起,我的回答晚了一年,但我认为这比其他两个简单很多(没有不必要的进口等)

func clen(n []byte) int {
    for i := 0; i < len(n); i++ {
        if n[i] == 0 {
            return i
        }
    }
    return len(n)
}

所以,

label := []byte{97, 98, 99, 0, 0, 0, 0}
s := label[:clen(label)]
fmt.Println(string(s))

这意味着将s设置为label中从clen(label)的开始到索引的字节片。

结果将是长度为3的CCD_ 5。

注意,第一个答案只适用于在null终止符之后只有一个零的字符串;然而,一个正确的C风格的以null结尾的字符串在第一个处结束,即使后面跟着垃圾。例如,[]byte{97,98,99,0,99,99,0}应该被解析为abc,而不是abc^@cc

要正确解析,请使用string.Index,如下所示,找到第一个,并使用它对原始字节切片进行切片:

package main
import (
    "fmt"
    "strings"
)
func main() {
    label := []byte{97,98,99,0,99,99,0}
    nullIndex := strings.Index(string(label), "x00")
    if (nullIndex < 0) {
        fmt.Println("Buffer did not hold a null-terminated string")
        os.Exit(1)
    }
    fmt.Println(string(label[:nullIndex]))
}

编辑:将缩短的版本打印为[]byte,而不是string。感谢@serbaut的接球。

编辑2:没有处理没有null终止符的缓冲区的错误情况。感谢@snap。

您可以使用sys包:

package main
import "golang.org/x/sys/windows"
func main() {
   b := []byte{97, 98, 99, 0, 0, 0, 0}
   s := windows.ByteSliceToString(b)
   println(s == "abc")
}

或者你可以自己实现:

package main
import "bytes"
func byteSliceToString(s []byte) string {
   n := bytes.IndexByte(s, 0)
   if n >= 0 {
      s = s[:n]
   }
   return string(s)
}
func main() {
   b := []byte{97, 98, 99, 0, 0, 0, 0}
   s := byteSliceToString(b)
   println(s == "abc")
}
  • https://pkg.go.dev/golang.org/x/sys/unix#ByteSliceToString
  • https://pkg.go.dev/golang.org/x/sys/windows#ByteSliceToString

1。strings.TrimSpace.TrimRight

//修剪尾部"\0",但无法处理类似"0"的字节;abc\x00def\x00"。

无法编辑@orelli的答案,所以在这里写道:

package main
import (
    "fmt"
    "strings"
)
func main() {
    label := string([]byte{97, 98, 99, 0, 0, 0, 0})
    s1 := strings.TrimSpace(label)
    fmt.Println(len(s1), s1)
    s2 := strings.TrimRight(label, "x00")
    fmt.Println(len(s2), s2)
  }

输出:

7 abc????
3 abc

//?是"\0",无法在此处显示。


因此
.TrimSpace无法修剪"\0",但
CCD_ 16;\x00";可以



2.字节。IndexByte

搜索第一个"\0",可能不支持utf-8

package main
import (
    "bytes"
    "fmt"
    "strings"
)
func main() {
    b_arr := []byte{97, 98, 99, 0, 100, 0, 0}
    label := string(b_arr)
    s1 := strings.TrimSpace(label)
    fmt.Println(len(s1), s1)   //7 abc?d??
    s2 := strings.TrimRight(label, "x00")
    fmt.Println(len(s2), s2)   //5 abc?d
    n := bytes.IndexByte([]byte(label), 0)
    fmt.Println(n, label[:n])  //3 abc
    s_arr := b_arr[:bytes.IndexByte(b_arr, 0)]
    fmt.Println(len(s_arr), string(s_arr)) //3 abc
}

等效

n1 := bytes.IndexByte(b_arr, 0)
n2 := bytes.Index(b_arr, []byte{0})
n3, c := 0, byte(0)
for n3, c = range b_arr {
    if c == 0 {
        break
    }
}

您可以使用bytes.SplitN并让它返回第一个子切片:

import (
    "bytes"
)
func bytesToStr(in []byte) string {
    str := bytes.SplitN(in, []byte{0}, 2)[0]
    return string(str)
}

在go 1.18+中,您还可以使用bytes.Cut:

func bytesToStr(in []byte) string {
    str, _, _ := bytes.Cut(in, []byte{0})
    return string(str)
}

最新更新