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:
- Find the first
0byte - Slice up to that position
- 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.
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.IndexByteif the first0means "end of string" - Use
bytes.TrimRightif 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
datais a fixed-size array of 10 bytes.- The first two bytes are
'G'and'o'. - The third byte is
0, which marks the end of meaningful text. data[:]converts the array into a slice so functions likebytes.IndexBytecan inspect it.bytes.IndexByte(data[:], 0)returns2because the first null byte is at index2.data[:n]becomesdata[:2], which contains only{'G', 'o'}.string(data[:n])converts those two bytes into the Go string"Go".fmt.Println(s)printsGo.
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
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
| Approach | What it does | Best for | Limitation |
|---|---|---|---|
string(data[:]) | Converts all bytes to a string | Raw byte-to-string conversion | Keeps null bytes |
bytes.IndexByte(data[:], 0) | Finds the first null byte | C-style null-terminated strings | Needs -1 handling |
bytes.TrimRight(data[:], "\x00") | Removes trailing null bytes | Zero-padded fixed-length fields | Does not stop at internal nulls |
| Manual loop | Scans until 0 byte | Full control or learning purposes | More verbose |
IndexByte vs TrimRight
IndexBytemeans: "the first0ends the string"TrimRightmeans: "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.
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
0bytes string(b)converts every byte inb- 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:
IndexBytestops there - Trailing null padding only:
TrimRightmay 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.
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.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.