Question

Convert a Zero-Terminated Byte Array to String in Go

Question

I need to read a [100]byte value that stores string data.

Not all strings are exactly 100 characters long, so the unused part of the byte array is padded with 0 bytes.

If I convert [100]byte to a string like this:

s := string(byteArray[:])

the trailing 0 bytes appear in the result as null characters.

In C, a string typically ends at the first 0 byte. What is the best way to convert this kind of zero-terminated byte array to a string in Go?

Short Answer

By the end of this page, you will understand why Go strings do not automatically stop at a null byte, how zero-terminated byte arrays differ from C strings, and how to safely convert fixed-size byte arrays like [100]byte into clean Go strings.

Concept

In Go, a string is a sequence of bytes. It can contain any byte value, including 0 (the null byte). That means Go does not treat 0 as a special end-of-string marker.

This is different from C, where strings are commonly stored as null-terminated character arrays. In C, the first \0 marks the end of the string.

So when you do this in Go:

s := string(byteArray[:])

Go converts all 100 bytes into the string, including any trailing 0 bytes.

If your data format uses a fixed-size byte array with zero padding, you need to manually find where the meaningful content ends.

A common approach is:

  1. Find the first 0 byte
  2. Slice up to that position
  3. Convert that slice to a string

This matters in real programs because many binary formats, network protocols, and C interop APIs store text in fixed-length buffers rather than true Go strings.

Mental Model

Think of the [100]byte array as a 100-slot storage tray.

  • The real text is placed in the first few slots
  • The remaining unused slots are filled with 0
  • Go reads the entire tray unless you tell it where to stop

C behaves more like: "stop reading when you hit the first empty marker."

Go behaves more like: "read exactly what you gave me."

So in Go, you must decide where the useful data ends before converting it to a string.

Take Quiz

Syntax and Examples

The core idea is to cut the byte slice at the first null byte.

Option 1: Use bytes.IndexByte

package main

import (
	"bytes"
	"fmt"
)

func main() {
	data := [100]byte{'H', 'e', 'l', 'l', 'o', 0, 0, 0}

	n := bytes.IndexByte(data[:], 0)
	if n == -1 {
		n = len(data)
	}

	s := string(data[:n])
	fmt.Println(s)
}

This finds the first 0 byte and converts only the bytes before it.

Option 2: Use bytes.TrimRight

If your array is padded with trailing zeros and you want to remove only zeros at the end:

package main

import (
	"bytes"
	"fmt"
)

func main() {
	data := [100]byte{'H', 'e', 'l', 'l', 'o', 0, 0, 0}

	s := string(bytes.TrimRight(data[:], "\x00"))
	fmt.Println(s)
}

This removes null bytes from the end only.

Which one should you use?

  • Use bytes.IndexByte if the first 0 means "end of string"
  • Use bytes.TrimRight if the format is specifically "text plus trailing zero padding"

For C-style null-terminated strings, bytes.IndexByte is usually the clearest choice.

Step by Step Execution

Consider this example:

package main

import (
	"bytes"
	"fmt"
)

func main() {
	data := [10]byte{'G', 'o', 0, 0, 0, 0, 0, 0, 0, 0}

	n := bytes.IndexByte(data[:], 0)
	if n == -1 {
		n = len(data)
	}

	s := string(data[:n])
	fmt.Println(s)
}

Step by step

  1. data is a fixed-size array of 10 bytes.
  2. The first two bytes are 'G' and 'o'.
  3. The third byte is 0, which marks the end of meaningful text.
  4. data[:] converts the array into a slice so functions like bytes.IndexByte can inspect it.
  5. bytes.IndexByte(data[:], 0) returns 2 because the first null byte is at index 2.
  6. data[:n] becomes data[:2], which contains only {'G', 'o'}.
  7. string(data[:n]) converts those two bytes into the Go string "Go".
  8. fmt.Println(s) prints Go.

If no null byte exists

If the array is completely filled with text and contains no 0, bytes.IndexByte returns -1. That is why this check is important:

if n == -1 {
	n = len(data)
}

Without it, slicing with data[:-1] would be invalid.

Real World Use Cases

This pattern appears often when Go works with older or lower-level systems.

Common situations

  • Binary file formats: fixed-size name fields padded with zeros
  • Network protocols: packet headers containing fixed-width text fields
  • C library interop: reading char[100] values from C structs
  • Database drivers or legacy systems: fixed-length text records
  • Embedded systems: device messages stored in byte buffers

Example scenarios

  • Reading a username from a binary record
  • Extracting a device label from firmware data
  • Parsing a file header that stores a 32-byte application name
  • Converting a C struct field into a normal Go string
Take Quiz

Real Codebase Usage

In real projects, developers usually wrap this logic in a helper function so they do not repeat it everywhere.

Helper function pattern

func cString(b []byte) string {
	n := bytes.IndexByte(b, 0)
	if n == -1 {
		n = len(b)
	}
	return string(b[:n])
}

This is common when parsing many fields from structs or binary data.

Guard clause pattern

Developers often handle the -1 case early:

func cString(b []byte) string {
	n := bytes.IndexByte(b, 0)
	if n < 0 {
		return string(b)
	}
	return string(b[:n])
}

Validation pattern

If the data must be valid UTF-8, some codebases validate after conversion:

import "unicode/utf8"

func safeText(b []byte) string {
	n := bytes.IndexByte(b, 0)
	if n == -1 {
		n = len(b)
	}
	s := string(b[:n])
	if !utf8.ValidString(s) {
		return ""
	}
	return s
}

Parsing structs and records

When reading binary records, the same helper may be used repeatedly:

name := cString(record.Name[:])
city := cString(record.City[:])
code := cString(record.Code[:])

This keeps parsing code clean and consistent.

Common Mistakes

1. Converting the full array directly

Broken example:

s := string(data[:])

This includes all bytes, including 0 padding.

Fix: stop at the first null byte.

n := bytes.IndexByte(data[:], 0)
if n == -1 {
	n = len(data)
}
s := string(data[:n])

2. Forgetting to handle -1

Broken example:

n := bytes.IndexByte(data[:], 0)
s := string(data[:n])

If no 0 exists, n is -1, and the slice will panic.

Fix: always check for -1.

3. Using TrimRight when the string may contain internal null bytes

s := string(bytes.TrimRight(data[:], "\x00"))

This only removes zeros from the end. It does not stop at the first null byte.

If your format is truly null-terminated, IndexByte is safer.

4. Expecting Go strings to behave like C strings

A Go string can contain 0 bytes without ending.

s := "A\x00B"
fmt.Println(len(s)) // 3

Go stores all three bytes.

5. Confusing arrays and slices

[100]byte is an array. Many utility functions work with []byte, so you often need:

data[:]

That creates a slice view over the array.

Comparisons

ApproachWhat it doesBest forLimitation
string(data[:])Converts all bytes to a stringRaw byte-to-string conversionKeeps null bytes
bytes.IndexByte(data[:], 0)Finds the first null byteC-style null-terminated stringsNeeds -1 handling
bytes.TrimRight(data[:], "\x00")Removes trailing null bytesZero-padded fixed-length fieldsDoes not stop at internal nulls
Manual loopScans until 0 byteFull control or learning purposesMore verbose

IndexByte vs TrimRight

  • IndexByte means: "the first 0 ends the string"
  • TrimRight means: "remove extra zeros from the end"

Manual loop version

func cString(b []byte) string {
	n := 0
	for n < len(b) && b[n] != 0 {
		n++
	}
	return string(b[:n])
}

This works the same way and is easy to understand for beginners.

Take Quiz

Cheat Sheet

// Convert a null-terminated byte slice to string
n := bytes.IndexByte(b, 0)
if n == -1 {
    n = len(b)
}
s := string(b[:n])

Quick rules

  • Go strings can contain 0 bytes
  • string(b) converts every byte in b
  • Go does not automatically stop at 0
  • Use bytes.IndexByte(b, 0) for C-style strings
  • Use bytes.TrimRight(b, "\x00") for trailing zero padding only
  • Convert arrays to slices with array[:]

Common helper

func cString(b []byte) string {
    n := bytes.IndexByte(b, 0)
    if n == -1 {
        n = len(b)
    }
    return string(b[:n])
}

Edge cases

  • No null byte present: use the full slice
  • Empty string: first byte is 0
  • Internal null byte: IndexByte stops there
  • Trailing null padding only: TrimRight may also work

FAQ

Why does Go not stop at \x00 like C?

Go strings are length-based, not null-terminated. A Go string can contain any bytes, including 0.

What is the best function for C-style strings in Go?

bytes.IndexByte(b, 0) is a clear and common choice because it finds the first null byte.

Can I use bytes.TrimRight instead?

Yes, if the data is zero-padded at the end. But it is not the same as stopping at the first null byte.

What happens if there is no 0 byte in the array?

Then the whole array is meaningful data, so you should convert the full slice.

Do I need to convert [100]byte to []byte first?

You do not need a copy, but you usually need a slice view: data[:].

Can a Go string contain null bytes safely?

Yes. Go strings can store null bytes without any problem.

Is there a standard library helper exactly for C strings?

For general byte handling, bytes.IndexByte is the usual tool. In some C interop cases, other packages may be relevant, but for plain byte arrays this is the standard approach.

Take Quiz

Mini Project

Description

Build a small Go program that reads fixed-size byte arrays representing names from a legacy data source. Each name is stored in a [16]byte buffer and padded with 0 bytes. Your task is to convert each buffer into a clean Go string and print the results.

Goal

Create a helper that converts zero-terminated byte arrays into readable Go strings and use it to process multiple records.

Requirements

  • Create a helper function that converts a null-terminated byte slice to a Go string.
  • Read at least three fixed-size byte arrays containing zero-padded names.
  • Correctly handle both padded and fully filled arrays.
  • Print the cleaned names without null characters.
Take Quiz

Keep learning

Related questions

View more