bytes, strings, strconv, unicode
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 oncebytes.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.Seekerstrconv: 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.