548

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Derrick Zhang
  • 21,201
  • 18
  • 53
  • 73
  • 3
    @AndréLaszlo: In the playground the `^@` doesn't show, but it would've been there if you'd test it in the terminal or something similar. The reason for this, is that Go does not stop converting the bytes array to a string when it finds a 0. `len(string(bytes))` in your example is 5 and not 1. It depends on the output function, whether the string is fully (with zeros) printed or not. – nemo Jan 12 '13 at 05:14
  • 8
    For the http response body, use `string(body)`. – Ivan Chau Jun 08 '15 at 09:51

11 Answers11

554

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)
Deleplace
  • 6,812
  • 5
  • 28
  • 41
Daniel
  • 38,041
  • 11
  • 92
  • 73
  • 2
    I know I'm a year late, but I should mention that *most* methods return the number of bytes read. For instance, binary.Read() can read into a [32]byte, but you don't know whether you've filled all 32 bytes or not. – Eric Lagergren Jan 02 '15 at 20:19
  • 7
    You should use [`bytes.IndexByte()`](https://golang.org/pkg/bytes/#IndexByte) which searches for a single `byte` instead of [`bytes.Index()`](https://golang.org/pkg/bytes/#Index) with a byte slice containing 1 byte. – icza Aug 07 '15 at 06:27
  • 59
    actually string(byteArray) will do too and will save a slice creation – throws_exceptions_at_you Nov 15 '15 at 11:26
  • 4
    Just to be clear though, this is casting a sequence of bytes to something that is _hopefully_ a valid UTF-8 string (and not say, Latin-1 etc., or some malformed UTF-8 sequence). Go will not check this for you when you cast. – Cameron Kerr Jun 10 '17 at 09:41
  • What if your byte array is in the reverse order aka little endian? – Sir Nov 22 '17 at 00:39
  • @Sir One of the many cool things about UTF-8 is that little and big endian systems can interpret the data the same way. It is usually processed one byte at a time. – Yobert Jan 15 '18 at 23:25
  • 2
    @CameronKerr From [blog.golang.org/strings](https://blog.golang.org/strings): "It's important to state right up front that a string holds arbitrary bytes. It is not required to hold Unicode text, UTF-8 text, or any other predefined format. As far as the content of a string is concerned, it is exactly equivalent to a slice of bytes." – user42723 Dec 21 '19 at 12:49
  • 1
    You actually need to convert the byteArray to slice even when using IndexByte n := bytes.IndexByte(byteArray[:], 0) – simonDos Mar 18 '20 at 13:36
375

Use:

s := string(byteArray[:])
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mattes
  • 8,936
  • 5
  • 48
  • 73
  • 3
    Cleanest way to convert the byte array for sure. I wonder if strings.Trim would help strip out the null bytes? http://golang.org/pkg/strings/#example_Trim – andyvanee Sep 21 '13 at 06:31
  • 29
    the question specifically says that `string(byteArray[:])` contains `^@` characters – Robert Aug 06 '14 at 01:53
  • 25
    What's the difference to `string(byteArray)`? Why you need to copy the array using `[:]`? – Robert Zaremba Nov 19 '15 at 12:03
  • 8
    @RobertZaremba > a string is in effect a read-only slice of bytes. You can't convert byte array directly to string so first slice then string. – ferhatelmas Nov 26 '15 at 14:57
  • 4
    @RobertZaremba For byte slices you don't need to add the `[:]`, for byte arrays, you do. – Drew LeSueur Apr 06 '17 at 16:22
  • 4
    Please read the question. It explicitly states that this does not terminate the string (as C would with say a strcpy) at the first null. It is very annoying that this incorrect answer has been so upvoted. The best answer IMO, is that mentioned above, with the use of IndexByte(). – Graham Nicholls Oct 16 '19 at 10:20
  • 1
    This is not correct, as the result string `s` has length 100 and contains many trailing 0 bytes (unprinted). See https://play.golang.org/p/y4AF-DFqp42 – Deleplace Jun 28 '21 at 12:09
69

Simplistic solution:

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

I'm not sure how performant this is though.

marcusljx
  • 959
  • 7
  • 9
16

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
peterSO
  • 158,998
  • 31
  • 281
  • 276
7

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.

T0xicCode
  • 4,583
  • 2
  • 37
  • 50
Paul Hankin
  • 54,811
  • 11
  • 92
  • 118
  • 8
    Your code doesn't compile and, even if it did, it won't work. A binary search algorithm finds the position of a specified value within a sorted array. The array is not necessarily sorted. – peterSO Jan 10 '13 at 00:07
  • @peterSO You are right, and in fact it is never sorted since it represents a bunch of meaningful names. – Derrick Zhang Jan 10 '13 at 06:22
  • 3
    If all the null bytes are at the end of the string a binary search works. – Paul Hankin Jan 10 '13 at 17:47
  • 6
    I don't understand the downvotes. The code compiles and is correct, assuming the string contains no \0 except at the end. The code's looking for \0, and under the assumptions of the question the array can be considered 'sorted', since all non-\0 precede all \0 and that's all the code is checking. If downvoters could find an example input on which the code doesn't work, then I'll remove the answer. – Paul Hankin Sep 21 '13 at 05:53
  • +1: having all nulls at the tail is a strong assumption, but keeping that (and considering Go performs initial zeroing) - it should work properly. I've not looked for typos, so I dont know if it compiles, I judge just from looking at the intent behind the code :) I think you should be very explicit about the assumptions. You'd best paste your last comment as a foreword before the code. – quetzalcoatl Jul 08 '14 at 00:05
  • 2
    Gives wrong result if input is `[]byte{0}`. In this case `FirstZero()` should return `0` so when slicing result would be `""`, but instead it returns `1` and slicing results in `"\x00"`. – icza Aug 07 '15 at 06:14
2

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

zach
  • 184
  • 2
  • 8
  • 1
    a) `bytes.Trim` takes a slice, not an array (you'd need `arr[:]` if arr is actually a `[100]byte` as the question states). b) `bytes.Trim` is the wrong function to use here. For input like `[]byte{0,0,'a','b','c',0,'d',0}` it will return "abc\x00d" instead of "" c) there already is a correct answer that uses `bytes.IndexByte`, the best way to find the first zero byte. – Dave C May 06 '19 at 12:07
1

Use this:

bytes.NewBuffer(byteArray).String()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 1
    Because a) the question says an array so at you'd need `byteArray[:]` since `bytes.NewBuffer` takes a `[]byte`; b) the question said the array has trailing zeros that you don't deal with; c) if instead your variable is a `[]byte` (the only way your line will compile) then your line is just a slow way of doing `string(v)`. – Dave C Jun 27 '15 at 15:30
0

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))
}
Laevus Dexter
  • 472
  • 4
  • 5
yuen
  • 65
  • 3
  • 1
    -1: Not sure if this is a serious answer, but you almost definitely do not want to invoke reflection and unsafe code just to convert a byte slice to string – Austin Hyde Aug 20 '18 at 22:16
  • 1
    A word of warning: using unsafe to convert a byte slice to a `string` may have serious implications if later the byte slice is modified. `string` values in Go are defined to be immutable, to which the entire Go runtime and libraries build on. You will teleport yourself into the middle of the most mysterious bugs and runtime errors if you go down this path. – icza Sep 04 '18 at 13:03
  • Edited, because this is against pointer usage (it has same behavior as direct casting, in the other words result will be not garbage collected). Read the paragraph (6) https://golang.org/pkg/unsafe/#Pointer – Laevus Dexter Oct 21 '19 at 12:06
  • seems this can't cut off '\0' in bytes: "abc\x00d\x00" got same as input. //go1.18 – yurenchen Sep 01 '22 at 15:11
0

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
manikawnth
  • 2,739
  • 1
  • 25
  • 39
-1
  • 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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
zzzz
  • 87,403
  • 16
  • 175
  • 139
  • The data are written by others and by other C language, and I only got to read it, so I cannot control the way it is written. – Derrick Zhang Jan 09 '13 at 07:50
  • 1
    Oh, then slice the byte array using a length value `s := a[:n]` or `s := string(a[:n])` if you need a string. If `n` is not directly available it must be computed, e.g. by looking for a specific/zero byte in the buffer (array) as Daniel suggests. – zzzz Jan 09 '13 at 08:00
-2

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")
}
Zombo
  • 1
  • 62
  • 391
  • 407