bytes, strings, strconv, unicode

Updated

September 8, 2026

bytes, strings, strconv, unicode

Overview

Text in Go is UTF-8 bytes with optional rune interpretation. This chapter is the stdlib toolkit for slicing, searching, converting, and validating that text without corrupting multi-byte characters.

Related language intro: Strings.

Package map

Package Use for
strings Immutable string ops, builders, readers
bytes Same ops on []byte (often I/O buffers)
strconv Parse/format numbers and quotes
unicode / unicode/utf8 Character classes and UTF-8 decode

Many strings functions have bytes twins (Contains, Split, TrimSpace, …). Prefer []byte when you already have buffers from I/O.

strings cookbook

strings.Contains(s, sub)
strings.HasPrefix(s, "http")
strings.Cut(s, "=")              // before, after, ok  (Go 1.18+)
strings.CutPrefix(s, "Bearer ")  // Go 1.20+
strings.CutLast(s, "/")          // last separator (Go 1.27+); bytes.CutLast twin
strings.Fields(s)                // split on unicode whitespace
strings.Join(parts, ",")
strings.ReplaceAll(s, old, new)
strings.ToValidUTF8(s, "\uFFFD")

Builder vs buffer

var b strings.Builder
b.Grow(64)
b.WriteString("id=")
b.WriteString(id)
out := b.String() // copies once

bytes.Buffer implements io.Writer and is better when mixing writes from encoders.

bytes cookbook

bytes.Contains(buf, []byte("ERROR"))
bytes.Split(buf, []byte{'\n'})
bytes.TrimSpace(buf)
reader := bytes.NewReader(buf) // io.Reader, io.Seeker

strconv: explicit conversions

n, err := strconv.Atoi("42")
n64, err := strconv.ParseInt("ff", 16, 64)
f, err := strconv.ParseFloat("3.14", 64)
b, err := strconv.ParseBool("true")

s := strconv.Itoa(42)
s = strconv.FormatInt(255, 16)          // "ff"
s = strconv.FormatFloat(f, 'f', 2, 64)  // "3.14"
q := strconv.Quote("a\nb")               // "\"a\\nb\""

Never use fmt.Sprintf("%d", n) in hot paths when strconv will do — it is clearer and often faster.

unicode and utf8

import (
    "unicode"
    "unicode/utf8"
)

utf8.RuneCountInString(s)
r, size := utf8.DecodeRuneInString(s)
unicode.IsLetter(r)
unicode.Is(unicode.Latin, r)

range over a string already yields runes; use utf8 when decoding partial buffers from the network.

Validation pattern

func isIdent(s string) bool {
    if s == "" {
        return false
    }
    for i, r := range s {
        if i == 0 && !unicode.IsLetter(r) && r != '_' {
            return false
        }
        if i > 0 && !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '_' {
            return false
        }
    }
    return utf8.ValidString(s)
}

Summary

Task API
Search / split strings strings.*
Same on buffers bytes.*
Number parse/format strconv.*
Rune properties unicode.*
Partial UTF-8 unicode/utf8

Runnable example

go mod init example
go run .
package main

import (
    "bytes"
    "fmt"
    "strconv"
    "strings"
    "unicode"
    "unicode/utf8"
)

func main() {
    s := "name=ada; role=admin"
    k, v, ok := strings.Cut(s, "; ")
    fmt.Println("cut:", k, "|", v, ok)

    var b strings.Builder
    b.WriteString("user:")
    b.WriteString(strings.ToUpper("ada"))
    fmt.Println("builder:", b.String())

    raw := []byte("  line1\nline2\n")
    fmt.Printf("trim=%q fields=%q\n", bytes.TrimSpace(raw), bytes.Fields(raw))

    n, err := strconv.ParseInt("2a", 16, 64)
    fmt.Println("parse:", n, err, "format:", strconv.FormatInt(n, 10))

    msg := "Go✓"
    fmt.Println("bytes", len(msg), "runes", utf8.RuneCountInString(msg))
    for _, r := range msg {
        fmt.Printf(" %q letter=%v\n", r, unicode.IsLetter(r))
    }

    fmt.Println("valid utf8?", utf8.ValidString("ok\xff"))
}

Expected output:

cut: name=ada | role=admin true
builder: user:ADA
trim="line1\nline2" fields=["line1" "line2"]
parse: 42 <nil> format: 42
bytes 5 runes 3
 'G' letter=true
 'o' letter=true
 '✓' letter=false
valid utf8? false

What to notice: - Cut is clearer than SplitN for single separators. - bytes and strings mirror each other — pick based on your data form. - Invalid UTF-8 is detectable; do not assume network input is clean.

Try next: Write splitKV(s string) (map[string]string, error) using strings.Cut on = and ; pairs, rejecting duplicates.