英文:
How can I convert a null-terminated string in a byte buffer to a string in Go?
问题
这个:
label := string([]byte{97, 98, 99, 0, 0, 0, 0})
fmt.Printf("%s\n", label)
变成了这样(^@
是空字节):
go run test.go
abc^@^@^@
英文:
This:
label := string([]byte{97, 98, 99, 0, 0, 0, 0})
fmt.Printf("%s\n", label)
does this (^@
is the null-byte):
go run test.go
abc^@^@^@
答案1
得分: 16
在Go的syscall包中有一个隐藏的函数,它可以找到第一个空字节([]byte{0})并返回其长度。我假设它被称为clen,表示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的abc
。
英文:
There's this function hidden inside Go's syscall package that finds the first null byte ([]byte{0}) and returns the length. I'm assuming it's called clen for C-Length.
Sorry I'm a year late on this answer, but I think it's a lot simpler than the other two (no unnecessary imports, etc.)
func clen(n []byte) int {
for i := 0; i < len(n); i++ {
if n[i] == 0 {
return i
}
}
return len(n)
}
So,
label := []byte{97, 98, 99, 0, 0, 0, 0}
s := label[:clen(label)]
fmt.Println(string(s))
What that ^ says is to set s
to the slice of bytes in label
from the beginning to the index of clen(label)
.
The result would be abc
with a length of 3.
答案2
得分: 12
请注意,第一个答案只适用于在空终止符后只有一串零的字符串;然而,一个正确的C风格的空终止字符串在第一个\0
处结束,即使后面跟着垃圾字符。例如,[]byte{97,98,99,0,99,99,0}
应该被解析为abc
,而不是abc^@cc
。
为了正确解析这个字符串,可以使用string.Index
函数来找到第一个\0
,然后使用它来切片原始的字节切片:
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:之前没有处理缓冲区没有空终止符的错误情况。感谢 @snap 的指正。
英文:
Note that the first answer will only work with strings that have only a run of zeroes after the null terminator; however, a proper C-style null-terminated string ends at the first \0
even if it's followed by garbage. For example, []byte{97,98,99,0,99,99,0}
should be parsed as abc
, not abc^@cc
.
To properly parse this, use string.Index
, as follows, to find the first \0
and use it to slice the original byte-slice:
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]))
}
EDIT: Was printing the shortened version as a []byte
instead of as a string
. Thanks to @serbaut for the catch.
EDIT 2: Was not handling the error case of a buffer without a null terminator. Thanks to @snap for the catch.
答案3
得分: 1
你可以使用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
英文:
You can use the sys
package:
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")
}
Or you can just implement it yourself:
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")
}
答案4
得分: 1
1. strings.TrimSpace .TrimRight
//trim tail '\0', but can't handle bytes like "abc\x00def\x00".
can't edit @orelli answer, so wrote here:
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)
}
output:
7 abc????
3 abc
// ? is '\0' which can't display here.
<br>
So
.TrimSpace
can't trim '\0', but
.TrimRight
with "\x00" can.
<br><br>
2. bytes.IndexByte
search for first '\0', maybe not support 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
}
equivalent
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
}
}
英文:
1. strings <del>.TrimSpace</del> .TrimRight
//trim tail '\0', but can't handle bytes like "abc\x00def\x00".
can't edit @orelli answer, so wrote here:
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)
}
output:
7 abc????
3 abc
// ? is '\0' which can't display here.
<br>
So
.TrimSpace
can't trim '\0', but
.TrimRight
with "\x00" can.
<br><br>
2. bytes.IndexByte
search for first '\0', maybe not support 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
}
equivalent
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
}
}
答案5
得分: 0
你可以使用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)
}
英文:
You can use bytes.SplitN
and have it return the first subslice:
import (
"bytes"
)
func bytesToStr(in []byte) string {
str := bytes.SplitN(in, []byte{0}, 2)[0]
return string(str)
}
In go 1.18+, you can also use bytes.Cut
:
func bytesToStr(in []byte) string {
str, _, _ := bytes.Cut(in, []byte{0})
return string(str)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论