Reading data from console in Go

Discover three essential methods for reading data from console in Go: fmt.Scan for single values, bufio.Reader for full lines, and bufio.Scanner for stream processing. Learn when to use each approach with practical examples and best practices.

Reading data from console in Go is essential for building interactive CLI applications. When we say reading data, there are many different sources from where we can read the data. For instance, we can read input from the screen (which is the focus of this post) and this is called standard input. Additionally, Go gives us the access to it by means of os.Stdin. Furthermore, whatever user types are piped in we will be able to access it.

There are three easy ways to read input in Go

  • Single Word / number (token)
  • A full Line (including spaces)
  • Many lines in a loop

Reading Single word/Number Input

We make use of fmt.Scan method for reading the inputs, for example inputs are Name age, we can read it like this

package main

import "fmt"

func main() {
	var name string
	var age int

	fmt.Print("Enter name and age (example: Siva 35): ")
	_, err := fmt.Scan(&name, &age)
	if err != nil {
		fmt.Println("Input error:", err)
		return
	}

	fmt.Printf("Hello %s! You are %d years old.\n", name, age)
}

func Scan

func Scan(a ...any) (n int, err error)

Scan scans text read from standard input, storing successive space-separated values into successive arguments. Newlines count as space. It returns the number of items successfully scanned. If that is less than the number of arguments, err will report why.[Official documentation]

In our program, we are omitting the second first parameter by using the literal _ and checking for any errors. Moreover, this pattern is common across the Go language and most of the error handling follows this approach.

The reference to the variable name and age is passed to Scanf where the method will read and assign the values. Pointer references are used here; you can read about this in operators. More information about this will be covered going forward.

  • You cannot input string with spaces

A full Line (including spaces)

bufio.Reader is used for reading full line and then parse the input , bufio wraps an ioReader and adds buffering helpful text mehtod

ReadString('\n') reads until the first occurrence of the delimiter (and includes it). If it hits an error before finding the delimiter, it returns what it read + the error (often io.EOF).

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	fmt.Print("Enter full name: ")
	fullName, err := reader.ReadString('\n')
	if err != nil {
		fmt.Println("Read error:", err)
		return
	}
	fullName = strings.TrimSpace(fullName)

	fmt.Print("Enter age: ")
	ageLine, err := reader.ReadString('\n')
	if err != nil {
		fmt.Println("Read error:", err)
		return
	}
	ageLine = strings.TrimSpace(ageLine)

	age, err := strconv.Atoi(ageLine)
	if err != nil {
		fmt.Println("Age must be a number:", err)
		return
	}

	fmt.Printf("Name=%q Age=%d\n", fullName, age)
}

Even thought this approach looks boring it is very predictable and easy to maintain.

Many lines in a loop

Scanner is great when you want to loop over input tokens (lines, words, bytes) using a split function.

Important limitation: scanning stops if a token is too large to fit in the scanner buffer. By default, the max token size is MaxScanTokenSize (64K).
You can increase the limit using scanner.Buffer(...), but you must do it before scanning starts (calling Buffer after scanning begins panics).

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	sc := bufio.NewScanner(os.Stdin)

	// Optional: allow longer lines than the default max token size.
	buf := make([]byte, 0, 64*1024)
	sc.Buffer(buf, 1024*1024) // up to 1MB tokens

	for sc.Scan() {
		fmt.Println("LINE:", sc.Text())
	}

	if err := sc.Err(); err != nil {
		fmt.Println("Scan error:", err)
	}
}

Which approach is better for reading data from console in Go?

“Better” depends on what your input looks like.

Quick decision guide

  • User types a few values (tokens)fmt.Scan / fmt.Scanln
    (fast to write, minimal code)
  • User types sentences or values with spacesbufio.Reader + parse
    (ReadString('\n') / ReadBytes('\n'))
  • You’re processing a stream line-by-line (stdin may be piped)bufio.Scanner
    (simple loop; watch token size)

A practical “default” recommendation

For most real CLI apps (prompts, validation, user-friendly errors), start with bufio.Reader.
Use fmt.Scan for quick demos.
Use bufio.Scanner for line-by-line stream processing.

However, we will not use these in practical application as there are other libraries that will help us to deal with reading input , command line arguments in a better way.

srnyapathi
srnyapathi
Articles: 41