15. Getting input
So far you have written every value yourself: predictable, but not interactive. This chapter reads what the user types into the terminal.
Go needs a few more lines for this than for printing, and three more packages. This chapter goes through them one at a time.
Reading one line
Here is the whole program, from
exercises/15/01-read-name.go:
// Chapter 15 example: read a name and greet.
// Run with: go run exercises/15/01-read-name.go
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Println("What is your name?")
text, _ := reader.ReadString('\n')
name := strings.TrimSpace(text)
fmt.Println("Hello, " + name)
}Run it. It prints the question, waits for your name and Enter, prints the greeting, then exits. Three lines do the new work:
reader := bufio.NewReader(os.Stdin)makes a variable that can read from the keyboard.os.Stdin("standard input") is what the user types into the terminal, andbufiois the package that reads it in convenient pieces. A program needs this line once, before its first read.text, _ := reader.ReadString('\n')waits for the user, and reads everything up to and including the end of the line.'\n'means "stop at the newline". It has single quotes because it is one character and not a string. Likestrconv.Atoiin Chapter 14, it gives back two values, and_throws away the second. Chapter 21 explains it.name := strings.TrimSpace(text)removes the line ending. The next section explains why you cannot skip this.
Open exercises/15/01-read-name.go and run it twice with
different names. Watch the greeting change to match.
Why TrimSpace is
required
ReadString keeps the line ending as part of the text. On
Windows, pressing Enter puts two characters at the end of the line:
\r and \n. So when you type
Keiko, text holds seven bytes, not five.
The marker %q prints a string with quotes around it and
its special characters visible.
exercises/15/05-line-ending.go uses it to show what
arrives. On Windows, typing 7 and pressing Enter
prints:
Type a number: 7
"7\r\n"
"7"
Without TrimSpace: 0
With TrimSpace: 7
strings.TrimSpace removes spaces, tabs, and line endings
from both ends of a string. Without it, two things go wrong:
- Text prints badly.
fmt.Println("Hello, " + text + "!")puts the!on the line below the name, because the line ending is insidetext. - Numbers come out as
0. Tostrconv.Atoi,"7\r\n"is not a number, as the last lines of the output above show.
So every read in this book is followed by
strings.TrimSpace.
The prompt on the same line
fmt.Println moves to a new line after the question, so
the user types below it. To keep the cursor on the same line, use
fmt.Print, which adds no new line, and end the prompt with
a space. Inside main, with the same imports:
reader := bufio.NewReader(os.Stdin)
fmt.Print("What is your name? ")
text, _ := reader.ReadString('\n')
name := strings.TrimSpace(text)
fmt.Println("Hello, " + name)Output:
What is your name? Keiko
Hello, Keiko
This is exercises/15/02-prompt-same-line.go.
Reading a number
ReadString always gives a string, even
for digits. To do maths, trim it and convert it with
strconv.Atoi from Chapter 14. Add "strconv" to
the imports:
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter a number: ")
text, _ := reader.ReadString('\n')
number, _ := strconv.Atoi(strings.TrimSpace(text))
fmt.Printf("Twice that is %d\n", number*2)The conversion line has two calls in one. The inner call runs first
and gives the trimmed string; the outer one turns it into an int. This
is exercises/15/03-read-number.go.
For a decimal, use strconv.ParseFloat the same way:
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter a decimal: ")
text, _ := reader.ReadString('\n')
x, _ := strconv.ParseFloat(strings.TrimSpace(text), 64)
fmt.Printf("Twice that is %.2f\n", x*2)If the user types something that is not a whole number, such as
abc, 1.5, or nothing at all,
strconv.Atoi gives 0. The program does
not crash and prints no message; it carries on with
0, as though the user had typed it.
strconv.ParseFloat does the same. The second value that
_ throws away is the only thing that reports the problem.
You will check it in Chapter 21. For now, type numbers when the program
asks for them, and if an answer comes out wrong, look at what you typed.
(One exception: a whole number too large for an int gives the largest
int, not 0.)
Reading several values
To read several values, call ReadString once per value,
always on the same reader:
reader := bufio.NewReader(os.Stdin)
fmt.Print("First number: ")
text, _ := reader.ReadString('\n')
a, _ := strconv.Atoi(strings.TrimSpace(text))
fmt.Print("Second number: ")
text, _ = reader.ReadString('\n')
b, _ := strconv.Atoi(strings.TrimSpace(text))
fmt.Printf("Sum: %d\n", a+b)Each call pauses and waits separately. The program does not continue
to the next line until Enter is pressed. This is
exercises/15/04-read-two.go.
Look closely at the second read. text already exists, so
it uses =, and _ is not a variable, so it does
not count as new. Writing := there stops with
no new variables on left side of :=. On the line below,
b, _ := is fine, because b is new.
You repeat these lines for each value. Chapter 21 shows how to put them in a function of your own and write them once.
Homework
Homework starters import only fmt, and their top comment
shows the imports you need. Remember that the Go extension for VS Code
may add them for you when you save.
Problem 1 — Greet by name
Open exercises/15/homework/01-greet-by-name.go. Prompt
for a first name with fmt.Print, read it, trim it, then
print Hello, <name>!. The exclamation mark is part of
the output.
Problem 2 — Sum of two
Open exercises/15/homework/02-sum-of-two.go. Prompt for
two numbers (separate prompts), convert each with
strconv.Atoi, then print the sum like this:
a + b = c
where a, b, c are the actual
values.
Problem 3 — Years to retirement
Open exercises/15/homework/03-retirement.go. Ask the
user's age and compute the years left until 65. Print:
You have N years until retirement.
(If someone is over 65, the number is negative. That is fine — no need to handle that case yet.)
Challenge — BMI
Open exercises/15/homework/04-bmi.go. Ask for height in
metres (a decimal like 1.78) and weight in kilograms (like
72.5). Use strconv.ParseFloat to convert both.
Compute Body Mass Index:
bmi := weight / (height * height)
Print the result to one decimal place using fmt.Printf
with %.1f. Label it clearly.
Stuck or finished? Open the homework solutions page.