The strings package
The same text tasks come up in almost every program: find a word, change the case, split a phrase into parts. For them, the Go standard library has the strings package.
Let's go back to the tile picture. The strings package is a set of tools for the row of tiles: find a pattern in it, swap tiles, cut the row into pieces and put them back together. None of the tools touches the old row, because strings are immutable. Each one builds a new row and hands it to you. Let's see the main tools at work:
package main
import (
"fmt"
"strings"
)
func main() {
s := "Go is fun"
fmt.Println(strings.Contains(s, "is")) // true
fmt.Println(strings.ToLower(s)) // go is fun
words := strings.Split(s, " ")
fmt.Println(len(words), strings.Join(words, "-")) // 3 Go-is-fun
}Let's go line by line:
Containsfinds"is"in the string and returnstrue.ToLowerreturns a lowercase copy of the string.Splitcuts the phrase at the spaces into three words.Joinglues them back with hyphens, and the program prints3 Go-is-fun.
Searching for a substring
strings.Contains(s, sub) returns true if the substring sub occurs in s. The search is case-sensitive:
strings.Contains("Go", "go")returnsfalse;strings.Contains("привет", "иве")returnstrue.
Changing the case
strings.ToLower and strings.ToUpper convert a string to lower and upper case, Cyrillic included: strings.ToUpper("мир") returns "МИР".
Strings are immutable, so every function of the package returns its result as a new string, and the original stays the same. To compare words regardless of case, compare their lowercase versions: strings.ToLower("Go") == strings.ToLower("GO") is true.
Splitting and joining
Three functions of the package cut a string into parts and put the parts back together. Let's look at each.
strings.Split
strings.Split(s, sep) cuts a string at a separator and returns a slice of strings, []string. It cuts at every separator, so:
- if spaces come in a row or at the edges,
Splitreturns empty strings between them; - if the separator does not occur in the string,
Splitreturns a slice of one element, the whole string.
Choose Split when the separator is exact and every part between separators matters, even an empty one.
strings.Join
strings.Join(words, sep) does the reverse: it glues a slice into one string and puts the separator between the elements. It takes only a slice of strings, []string. The empty separator "" glues the parts tightly.
strings.Fields
strings.Fields(s) cuts at any number of spaces and returns no empty strings. It also treats tabs and line breaks as spaces. Choose Fields when you need the words and the number of spaces between them does not matter. Let's compare it with Split on one string:
s := " go is "
fmt.Println(len(strings.Split(s, " ")), len(strings.Fields(s))) // 5 2
fmt.Println(strings.Fields(s)) // [go is]
fmt.Println(strings.Join([]string{"go", "is"}, "")) // goisLet's walk through the example line by line:
- The first
Printlnprints5 2:Splitreturned five parts, andFieldstwo words. - The second
Printlnprints[go is]:Fieldskept only the words. - The third
Printlnprintsgois: the empty separator glued the parts tightly.
The five parts from Split are:
- an empty string before the first space;
"go";- an empty string between the spaces;
"is";- an empty string at the end.
So strings.Join(strings.Fields(s), " ") removes extra spaces from a phrase: Fields finds the words, and Join puts exactly one space between them.
Building a string in a loop
Interviewers like to ask why a long string is not built with += in a loop. Let's compare the two ways.
The += operator
A string is immutable, so every s += w creates a new string and copies all the bytes gathered so far into it. For a thousand words that is a thousand copies, each longer than the last. For a few parts outside a loop, the + operator is fine: there are few copies.
strings.Builder
The strings.Builder type gathers the bytes in one place, the way append gathers the elements of a slice:
words := []string{"go", "is", "fun"}
var b strings.Builder
for _, w := range words {
b.WriteString(w)
}
fmt.Println(b.String()) // goisfunLet's walk through the example line by line:
var b strings.Buildersets up an empty builder: its zero value is ready for use.b.WriteString(w)in the loop appends a word to the end without copying everything gathered before.b.String()returns the finished string, and the program printsgoisfun.
Common mistake
A package function is called in the hope that the string itself changes:
s := "go"
strings.ToUpper(s) // ✗
fmt.Println(s) // goThe program builds and prints go: ToUpper returned a new string, and nothing kept it. Unlike with append, the compiler does not report the unused result here, so the mistake shows only in the output. Assign the result to the variable: s = strings.ToUpper(s).
Key points
- The functions of the
stringspackage never change a string; they return a new one, and you must keep the result. Splitcuts at every separator and keeps empty strings, whileFieldscuts at spaces with no empty strings.Joinglues a slice of strings with a separator.- Build a long string in a loop with
strings.Builder: every+=copies the whole string again.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.