14. Working with text
Chapter 12 covered what Go gives you directly for strings: joining
with +, length with len, indexing, and
slicing. This chapter adds the tools for changing case, repeating,
searching and replacing, which live in the
strings package. It also covers
fmt.Sprintf and the fmt markers, and the
strconv package, which moves between text
and numbers.
Using the strings
package
Import strings next to fmt:
import (
"fmt"
"strings"
)Functions in it take the string to work on as an argument. Those that produce text give back a new string. The original is never changed, because in Go strings cannot be changed.
Changing case
strings.ToUpper and strings.ToLower give a
new string in upper or lower case:
name := "Keiko"
fmt.Println(strings.ToUpper(name)) // KEIKO
fmt.Println(strings.ToLower(name)) // keiko
fmt.Println(name) // Keiko (still the original)Repeating a string
strings.Repeat repeats a string that many times:
fmt.Println(strings.Repeat("-", 20))
fmt.Println(strings.Repeat("ab", 3))
fmt.Println(strings.Repeat("=", 4) + " TITLE " + strings.Repeat("=", 4))Output:
--------------------
ababab
==== TITLE ====
It is the right tool for drawing borders or padding text. These
examples are in exercises/14/02-case-and-repeat.go.
Searching with
Contains and Index
The simplest way to check whether a string contains something is
strings.Contains, which gives true or
false:
fmt.Println(strings.Contains("hello world", "world")) // true
fmt.Println(strings.Contains("hello world", "z")) // falseBecause it gives a bool, it pairs naturally with if,
which Chapter 16 teaches. For now, print the result.
When you also need to know where the match is, use
strings.Index. It gives the index of the first match, or
-1 if there is none:
fmt.Println(strings.Index("hello world", "world")) // 6
fmt.Println(strings.Index("hello world", "z")) // -1To test for presence, use strings.Contains — it is
clearer. Use strings.Index when you need the position
number.
Replacing with
ReplaceAll
strings.ReplaceAll(s, old, new) gives a new string with
every occurrence of old swapped for
new:
fmt.Println(strings.ReplaceAll("hello", "l", "L")) // heLLo
fmt.Println(strings.ReplaceAll("a-b-c", "-", " ")) // a b cThe original string is untouched. If you want to keep the result, store it:
message := "meet me at noon"
dashed := strings.ReplaceAll(message, " ", "-")
fmt.Println(dashed) // meet-me-at-noonThe searching and replacing examples are in
exercises/14/03-search-and-replace.go.
strings.Replace does the same but takes a count as a
fourth argument, the most replacements to make.
strings.Replace("hello", "l", "L", 1) gives
heLlo.
Other useful functions
The strings package has many functions. A few you will
reach for often:
| Function | What it does |
|---|---|
strings.TrimSpace(s) |
Remove leading and trailing whitespace. |
strings.Fields(s) |
Split on whitespace, into a list of words. |
strings.Split(s, sep) |
Split on a specific separator. |
strings.HasPrefix(s, t) |
true if s starts with t. |
strings.HasSuffix(s, t) |
true if s ends with t. |
padded := " hello "
fmt.Println(strings.TrimSpace(padded)) // hello
words := strings.Fields("one two three")
fmt.Println(words) // [one two three]
parts := strings.Split("a,b,c", ",")
fmt.Println(parts) // [a b c]strings.Fields and strings.Split give back
a list of values, also called a slice, which Chapter 22
covers. For now, just print it.
Formatting with
fmt.Printf and fmt.Sprintf
When you need numbers or text plugged into specific spots,
fmt.Printf is the standard approach. You have met some of
its markers already. Go calls them verbs:
| Verb | What it does | Example output |
|---|---|---|
%s |
a string | Keiko |
%d |
a whole number | 7 |
%.2f |
a float64 with two decimals | 3.14 |
%v |
any value, in its usual form | Keiko or 7 |
%T |
the type of the value | float64 |
%-10s |
a string, padded to width 10 | Keiko |
%5d |
a number, padded to width 5 | 7 |
name := "Keiko"
level := 7
hp := 95
fmt.Printf("%s (Lv %d) HP %d\n", name, level, hp)Output:
Keiko (Lv 7) HP 95
fmt.Sprintf takes the same arguments
but does not print. It gives you the finished string, to store or join
(the S is for "string"):
name := "Keiko"
level := 7
line := fmt.Sprintf("%s is level %d", name, level)
fmt.Println(line) // Keiko is level 7A wrong verb is not a compile error, so the program
runs and prints something odd where the value should be.
fmt.Printf("%d\n", "Keiko") prints
%!d(string=Keiko). If you see %! in your
output, compare the verbs with the values after them.
Open exercises/14/01-format.go. Change the first
fmt.Printf so the output reads
Keiko has 95 HP at level 7.
Turning numbers into text and text into numbers
The strconv package ("string conversion") moves between
the two. strconv.Itoa turns an int into a string, which
lets you use +:
level := 7
fmt.Println("Level " + strconv.Itoa(level) + " complete") // Level 7 completestrconv.Atoi goes the other way, and it gives back
two values: the number, and a report of whether the
conversion worked. You met two values on one line in Chapter 11. Chapter
21 explains the report, which is called an error. Until then, throw it
away by writing _, the blank identifier,
in its place. Unlike a variable, _ never has to be
used:
n, _ := strconv.Atoi("42")
fmt.Println(n + 1) // 43For a decimal, use strconv.ParseFloat. The second
argument, 64, means you want a float64:
x, _ := strconv.ParseFloat("3.14", 64)
fmt.Println(x * 2) // 6.28If the text cannot become a number, the result is 0,
with no crash:
n, _ := strconv.Atoi("hello")
fmt.Println(n) // 0Throwing away the report means your program cannot tell this
0 from a real 0. That is acceptable for now,
and Chapter 21 fixes it.
You will use these conversions a lot in the next chapter, where the
program reads keyboard input. Whatever the user types arrives as a
string, even if they typed digits. All of this is in
exercises/14/04-text-and-numbers.go.
string(level) looks like the right conversion, but it is
not. It gives the character stored under that number, not the digits.
strconv.Itoa is the one that turns 7 into
"7".
Homework
Homework files are in exercises/14/homework/.
Problem 1 — Loud and quiet
Open exercises/14/homework/01-loud-and-quiet.go. A
starter variable holds a name. Print it three times: as it is, in upper
case, then in lower case.
Problem 2 — Contains
Open exercises/14/homework/02-contains.go. A variable
holds a sentence. Use strings.Contains to print whether it
contains the word go, then whether it contains the word
cake.
Problem 3 — Censor
Open exercises/14/homework/03-censor.go. A variable
holds a short message. Use strings.ReplaceAll to swap every
space with a dash -, then print it.
Challenge — Title block
Open exercises/14/homework/04-title-block.go. A variable
holds a title (any string you like). Print a block like this, where the
top and bottom dash rows match the length of the row with the title:
---------------
-- INVENTORY --
Inside text goes here
-- INVENTORY --
---------------
Use strings.Repeat and len to size the
borders without counting by hand. Changing the title should change the
border length automatically.
Stuck or finished? Open the homework solutions page.