strings

Guided tour · Formatting & Strings · pkg.go.dev →

Search, split, join, replace, case conversion. Works on UTF-8-encoded strings.

Read, search, and reshape UTF-8 text. For mutable byte buffers see bytes; for splitting on patterns see regexp.

Contains a substring
strings.Contains(s, "needle")
Replace all occurrences
strings.ReplaceAll(s, "old", "new")
Split on a separator
parts := strings.Split(line, ",")
Join a slice
strings.Join(parts, ", ")
Trim whitespace
strings.TrimSpace(s)
Lower / upper case
strings.ToLower(s)
Has prefix / suffix
strings.HasPrefix(s, "http://")
Build a long string fast
var b strings.Builder
b.WriteString("hello, ")
b.WriteString(name)
result := b.String()

Searching and testing

Contains, HasPrefix, HasSuffix

strings.Contains("gopher", "ph")    // true
strings.HasPrefix("gopher", "go")   // true
strings.HasSuffix("gopher", "her")  // true
strings.ContainsAny("abc", "xyzb")  // true — any of the runes
strings.ContainsRune("abc", 'b')    // true

Index family — position of first match, or -1

Pair Index with slicing when you need to split on something custom.

strings.Index("gopher", "ph")       // 2
strings.LastIndex("banana", "a")    // 5
strings.IndexAny("abc", "xyb")      // 1 — first rune from set
strings.IndexByte("abc", 'c')       // 2

Count

strings.Count("cheese", "e")  // 3
strings.Count("abc", "")      // 4 — len(s) + 1 for empty substring

EqualFold — case-insensitive equality

Prefer EqualFold over lowercasing both sides — it's cheaper and Unicode-correct.

strings.EqualFold("Go", "GO")  // true

Split and join

Split, SplitN, SplitAfter

Split drops the separator; SplitAfter keeps it. SplitN caps the number of pieces.

strings.Split("a,b,c", ",")          // ["a" "b" "c"]
strings.SplitN("a,b,c", ",", 2)      // ["a" "b,c"]
strings.SplitAfter("a,b,c", ",")     // ["a," "b," "c"]
strings.Fields("  hello   world  ")  // ["hello" "world"] — any whitespace

Join

strings.Join([]string{"a", "b", "c"}, ", ")  // "a, b, c"

Cut — idiomatic single split

Cut is the modern (1.18+) way to split on a separator once. Cleaner than SplitN(2).

k, v, ok := strings.Cut("name=ada", "=")
fmt.Println(k, v, ok)
k, v, ok = strings.Cut("bare", "=")
fmt.Println(k, v, ok)  // "bare", "", false
Output
name ada true
bare  false

CutPrefix / CutSuffix (1.20+)

s, ok := strings.CutPrefix("go-mod", "go-")  // "mod", true
s, ok = strings.CutSuffix("app.log", ".log") // "app", true

Transformation

Case conversion

strings.ToUpper("Go")        // "GO"
strings.ToLower("Go")        // "go"
strings.Title("hello world") // DEPRECATED — use cases package
strings.ToValidUTF8("a\xffb", "?")  // "a?b"

TrimSpace, Trim, TrimLeft/Right

Trim removes any runes in the cutset, not a substring. Use TrimPrefix/TrimSuffix for exact matches.

strings.TrimSpace("  hi\n")        // "hi"
strings.Trim("--hi--", "-")        // "hi"
strings.TrimLeft("000123", "0")    // "123"
strings.TrimPrefix("go-mod", "go-") // "mod"
strings.TrimSuffix("a.log", ".log") // "a"

Replace and ReplaceAll

strings.Replace("aaaa", "a", "b", 2)  // "bbaa"
strings.ReplaceAll("aaaa", "a", "b")  // "bbbb"

Map — per-rune transformation

rot13 := func(r rune) rune {
    switch {
    case r >= 'A' && r <= 'Z':
        return 'A' + (r-'A'+13)%26
    case r >= 'a' && r <= 'z':
        return 'a' + (r-'a'+13)%26
    }
    return r
}
strings.Map(rot13, "Hello, Gophers!")  // "Uryyb, Tbcuref!"

Repeat

strings.Repeat("ab", 3)  // "ababab"

Building strings efficiently

Concatenation with + allocates each time. For loops of writes, use strings.Builder.

strings.Builder — zero-copy final string

Builder writes into a growing []byte and hands it out as a string without copying. Don't copy Builder values after first use.

var b strings.Builder
for i := 0; i < 3; i++ {
    fmt.Fprintf(&b, "line %d\n", i)
}
fmt.Print(b.String())
Output
line 0
line 1
line 2

Reader — read from a string

strings.NewReader gives you an io.Reader/Seeker without a copy. Useful for APIs that take readers.

r := strings.NewReader("hello")
buf := make([]byte, 3)
n, _ := r.Read(buf)
fmt.Printf("%d %s\n", n, buf[:n])
Output
3 hel

Replacer — multiple replacements, one pass

NewReplacer

Much faster than chained ReplaceAll calls when you have many pairs. Safe to reuse.

r := strings.NewReplacer("<", "&lt;", ">", "&gt;", "&", "&amp;")
fmt.Println(r.Replace("<b>A & B</b>"))
Output
&lt;b&gt;A &amp; B&lt;/b&gt;