12. Strings
Strings are the values you print most. Chapter 07 showed the two ways
to write one: between double quotes, "like this", and
between backticks, `like this`. This chapter covers what Go
gives you directly for strings: joining, measuring, reading a single
position, cutting out a piece, and writing special characters.
Joining strings with +
You can join two strings with + (the
concatenation operator). It gives one new string with
the second stuck onto the end of the first:
first := "Hello, "
second := "world!"
fmt.Println(first + second) // Hello, world!The original variables are untouched — + builds a new
string.
Go's + does not convert numbers to strings. Adding a
number to a string is a compile error:
level := 7
fmt.Println("Level " + level)invalid operation: "Level " + level (mismatched types untyped string and int)
The fix for now is fmt.Printf from Chapter 09, which
takes numbers directly:
level := 7
fmt.Printf("Level %d complete\n", level) // Level 7 completeChapter 14 shows how to turn a number into a string, when you need one.
Length with len
The len function gives a string's length:
fmt.Println(len("hello")) // 5
fmt.Println(len("")) // 0
greeting := "Welcome"
fmt.Println(len(greeting)) // 7Use len whenever you need a string's length — for
centring text, checking input length, or building borders.
Open exercises/12/01-length.go. Add a line that prints
the length of your own name.
Strings are bytes
The last section did not say what len counts. It counts
bytes, the small units a computer stores text in. A
plain English letter, digit, or space takes one byte, so for text like
"hello", bytes and characters come to the same number.
Letters with accents take more:
fmt.Println(len("e")) // 1
fmt.Println(len("é")) // 2To count characters instead of bytes, use
RuneCountInString from the unicode/utf8
package. A rune is Go's word for one character:
word := "café"
fmt.Println(len(word)) // 5
fmt.Println(utf8.RuneCountInString(word)) // 4This needs a second import. To import more than one package, put them in parentheses, one per line:
import (
"fmt"
"unicode/utf8"
)The full program is exercises/12/03-bytes.go. Most text
in this book is plain English letters, so the rest of Part 3 uses
len. Keep in mind that it can be larger than the number of
characters you see.
Indexing
Each byte in a string has a position, called its index. Square brackets read the byte at one position:
word := "dragon"
fmt.Println(word[0]) // 100Two surprises here. Indexes start at 0, not 1, so
word[0] is the first position. And the result is not the
letter d but 100, the number that stores
d, because indexing gives you a byte. To get text, use a
slice, which is the next section.
Go strings are 0-indexed. If you want the first position, use
word[0], not word[1]. Asking for
word[1] gives the second one. This will feel wrong
at first; it becomes natural quickly.
Slicing
A slice of a string gives you part of it, as a new
string. The syntax is s[start:stop], where
start is the index of the first byte you want and
stop is the index after the last one:
word := "dragon"
fmt.Println(word[0:3]) // dra
fmt.Println(word[3:]) // gon
fmt.Println(word[:1]) // dLeaving out stop means "to the end of the string", and
leaving out start means "from the beginning". So
word[:1] is the first letter as a string.
Go has no negative indexes. word[-3:] stops with
invalid argument: index -3 (constant of type int) must not be negative.
To count from the end, use len:
word := "dragon"
fmt.Println(word[len(word)-3:]) // gon
fmt.Println(word[len(word)-1:]) // nAsking for a position past the end is an error the compiler usually cannot catch, so it happens while the program runs. The program stops and prints a message such as:
panic: runtime error: slice bounds out of range [:10] with length 6
Slicing counts bytes, like len and indexing. In
"café" the é takes two bytes, at indexes 3 and
4. word[:3] gives caf, but
word[:4] cuts the é in half and the program
prints a broken character. For plain English letters this never comes
up.
Open exercises/12/02-index-and-slice.go. A variable
holds a word. Using slicing, print just its first letter, then just its
last.
Strings cannot be changed
You can give a string variable a new string, but you cannot change a byte inside a string:
word := "dragon"
word[0] = 'D'
fmt.Println(word)cannot assign to word[0] (neither addressable nor a map index expression)
To get a changed version, build a new string from the pieces and assign it:
word := "dragon"
word = "D" + word[1:]
fmt.Println(word) // DragonEscape sequences
Some characters are awkward in a string because they collide with
Go's punctuation. The fix is an escape sequence — a
backslash \ followed by a character, which Go replaces with
the special character:
| Sequence | What it becomes |
|---|---|
\n |
a newline (move to next line) |
\t |
a tab |
\" |
a literal double quote |
\\ |
a literal backslash |
A single quote needs no escape inside double quotes, so write
"it's". Writing "it\'s" stops with
unknown escape.
Example:
fmt.Println("Line 1\nLine 2\nLine 3")
fmt.Println("Name\tLevel\tHP")
fmt.Println("She said \"hi\" and waved.")Output:
Line 1
Line 2
Line 3
Name Level HP
She said "hi" and waved.
Multi-line strings
When a string spans many lines with lots of quotes, escape sequences get noisy. A string between backticks is a raw string: anything between them is the string, including newlines and quotes, and escape sequences are not replaced.
poem := `
Roses are red,
Violets are blue,
"Pick a number,"
said the program to you.
`
fmt.Println(poem)Once created, a raw string is just a regular string — a friendlier
way to write it. The string starts with a new line and ends with one,
because the backticks sit on their own lines. These examples are in
exercises/12/04-escapes.go.
Because \n is not replaced in a raw string,
`a\nb` prints the four characters a\nb on one
line. This makes backticks handy for text with many backslashes, such as
a Windows path.
Homework
Problem 1 — Initials
Open exercises/12/homework/01-initials.go. Two variables
hold a first and last name. Using slicing, print the person's initials,
like K.R. for "Keiko Raharja".
Problem 2 — Stat line
Open exercises/12/homework/02-stat-line.go. Three
variables are declared at the top: a string name, an int level, an int
hp. Using fmt.Printf, print one line in this exact
shape:
Keiko Lv 7 HP 95
The labels (Lv, HP) and the spacing between
fields are literal spaces inside the string you give
fmt.Printf.
Problem 3 — One-line three-line poem
Open exercises/12/homework/03-three-line-poem.go. Print
a three line poem using exactly one
fmt.Println call. The line breaks must come from
\n inside the string. Add a // comment after
the call explaining why one call can produce three lines.
Challenge — Last letters
Open exercises/12/homework/04-last-letters.go. A
variable holds a word. Using a slice and len, print its
last three characters, then print its length. Make it work for any
plain-letter word of at least three letters without counting by
hand.
Stuck or finished? Open the homework solutions page.