Reading Input
Command-line programs often need to read what the user types, or data piped in from another program. Go offers two main tools: fmt.Scan for quickly reading whitespace-separated values, and bufio.Scanner for reading line by line — the one you'll use most. This lesson covers both, plus converting and validating what you read.
Standard input
A running program has three standard streams: standard input (os.Stdin, normally the keyboard), standard output (os.Stdout, the terminal) and standard error (os.Stderr, also the terminal, for messages). Standard input can also come from a file or another program:
go run . < names.txt # read from a file echo "Ana" | go run . # read another program's output
In this lesson each example shows the input it was given in an Input box. When you type input yourself, it appears on screen as you type; in the outputs here it doesn't, because it came from a file.
Reading lines with bufio.Scanner
A bufio.Scanner splits its input into lines. Each call to Scan reads the next line and returns false at the end of the input; Text returns the line without its newline:
package main import ( "bufio" "fmt" "os" ) func main() { scanner := bufio.NewScanner(os.Stdin) fmt.Print("What's your name? ") scanner.Scan() name := scanner.Text() fmt.Print("Favourite colour? ") scanner.Scan() colour := scanner.Text() fmt.Printf("Hello, %s! %s is a great colour.\n", name, colour) }
Ana Lima dark green
What's your name? Favourite colour? Hello, Ana Lima! dark green is a great colour.
Reading whole lines means names and phrases with spaces come through intact.
Reading until the input ends
Put Scan in a loop to process every line. After the loop, scanner.Err() reports any read error (reaching the end of the input is not an error):
package main import ( "bufio" "fmt" "os" "strings" ) func main() { scanner := bufio.NewScanner(os.Stdin) lines, words := 0, 0 for scanner.Scan() { line := scanner.Text() lines++ words += len(strings.Fields(line)) } if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "read error:", err) os.Exit(1) } fmt.Printf("%d lines, %d words\n", lines, words) }
The quick brown fox jumps over the lazy dog
3 lines, 9 words
When typing at the keyboard, end the input with Ctrl+D (macOS, Linux) or Ctrl+Z then Enter (Windows). strings.Fields splits a line on any run of spaces or tabs.
To read word by word instead of line by line, call scanner.Split(bufio.ScanWords) before the loop. Lines longer than 64 KB make the scanner stop with an error; for huge lines, give it a bigger buffer with scanner.Buffer.
Reading numbers
Input arrives as text. Convert it with strconv and check the error, because users type all sorts of things:
package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { scanner := bufio.NewScanner(os.Stdin) total, count := 0.0, 0 for scanner.Scan() { text := strings.TrimSpace(scanner.Text()) if text == "" { continue } price, err := strconv.ParseFloat(text, 64) if err != nil { fmt.Printf("skipping %q: not a number\n", text) continue } total += price count++ } fmt.Printf("%d prices, total %.2f\n", count, total) }
4.50 12.99 three 3.25
skipping "three": not a number 3 prices, total 20.74
Quick input with fmt.Scan
fmt.Scan reads space-separated values straight into variables, converting them to each variable's type. You pass pointers to the variables (&x, lesson 37) so Scan can fill them in. It returns how many values it read and an error:
package main import "fmt" func main() { var name string var age int fmt.Print("Name and age: ") n, err := fmt.Scan(&name, &age) if err != nil { fmt.Println("\nbad input:", err, "- read", n, "value(s)") return } fmt.Printf("\n%s will be %d next year\n", name, age+1) }
Ana thirty
Name and age: bad input: expected integer - read 1 value(s)
fmt.Scan is handy for exercises, but it treats newlines like spaces, stops at the first error and leaves the rest of the input unread, which makes recovering from bad input awkward. For real programs, read lines with a Scanner and parse them yourself, as above.
Try it yourself
Read a list of test scores, one per line, and print the highest, lowest and average.
72 95 61 88
min 61, max 95, average 79.0
if.