How can I convert a zero-terminated byte array to string?

Go

Go Problem Overview


I need to read [100]byte to transfer a bunch of string data.

Because not all of the strings are precisely 100 characters long, the remaining part of the byte array is padded with 0s.

If I convert [100]byte to string by: string(byteArray[:]), the tailing 0s are displayed as ^@^@s.

In C, the string will terminate upon 0, so what's the best way to convert this byte array to string in Go?

Go Solutions


Solution 1 - Go

Methods that read data into byte slices return the number of bytes read. You should save that number and then use it to create your string. If n is the number of bytes read, your code would look like this:

s := string(byteArray[:n])

To convert the full string, this can be used:

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

This is equivalent to:

s := string(byteArray[:])

If for some reason you don't know n, you could use the bytes package to find it, assuming your input doesn't have a null character embedded in it.

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

Or as icza pointed out, you can use the code below:

n := bytes.IndexByte(byteArray[:], 0)

Solution 2 - Go

Use:

s := string(byteArray[:])

Solution 3 - Go

Simplistic solution:

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

I'm not sure how performant this is though.

Solution 4 - Go

For example,

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)
}

Output:

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

Solution 5 - Go

The following code is looking for '\0', and under the assumptions of the question the array can be considered sorted since all non-'\0' precede all '\0'. This assumption won't hold if the array can contain '\0' within the data.

Find the location of the first zero-byte using a binary search, then slice.

You can find the zero-byte like this:

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))
}

It may be faster just to naively scan the byte array looking for the zero-byte, especially if most of your strings are short.

Solution 6 - Go

When you do not know the exact length of non-nil bytes in the array, you can trim it first:

> string(bytes.Trim(arr, "\x00"))

Solution 7 - Go

Only use for performance tuning.

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))
}

Solution 8 - Go

Use this:

bytes.NewBuffer(byteArray).String()

Solution 9 - Go

Though not extremely performant, the only readable solution is:

  // 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]

A full example to have a C-style byte array:

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)
}

A full example to have a Go style string excluding the nulls:

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)
}

This allocates a slice of slice of bytes. So keep an eye on performance if it is used heavily or repeatedly.

Solution 10 - Go

  • Use slices instead of arrays for reading. For example, io.Reader accepts a slice, not an array.

  • Use slicing instead of zero padding.

Example:

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

Solution 11 - Go

Here is an option that removes the null bytes:

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")
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDerrick ZhangView Question on Stackoverflow
Solution 1 - GoDanielView Answer on Stackoverflow
Solution 2 - GomattesView Answer on Stackoverflow
Solution 3 - GomarcusljxView Answer on Stackoverflow
Solution 4 - GopeterSOView Answer on Stackoverflow
Solution 5 - GoPaul HankinView Answer on Stackoverflow
Solution 6 - GozachView Answer on Stackoverflow
Solution 7 - GoyuenView Answer on Stackoverflow
Solution 8 - GoBhagya Prasad NRView Answer on Stackoverflow
Solution 9 - GomanikawnthView Answer on Stackoverflow
Solution 10 - GozzzzView Answer on Stackoverflow
Solution 11 - GoZomboView Answer on Stackoverflow