Question

How to Read Standard Input in Go

Question

I want to read standard input from the command line in Go, similar to Console.ReadLine() in C#. However, my program often exits before I can enter anything. What is the correct way to read console input in Go?

Here is my current code:

package main

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

func main() {
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("Enter text: ")
    text, _ := reader.ReadString('\n')
    fmt.Println(text)

    fmt.Println("Enter text: ")
    text2 := ""
    fmt.Scanln(text2)
    fmt.Println(text2)

    ln := ""
    fmt.Sscanln("%v", ln)
    fmt.Println(ln)
}

I am looking for the Go equivalent of reading a full line from standard input, and I also want to understand why the other attempts do not work as expected.

Short Answer

By the end of this page, you will understand how to read input from standard input in Go, when to use bufio.Reader versus fmt.Scanln, and why some input attempts fail. You will also learn beginner-friendly patterns for reading full lines, single values, and handling common input mistakes.

Concept

In Go, reading from standard input means reading data from os.Stdin, which represents what the user types in the terminal.

There are two common approaches:

  • bufio.Reader for reading full lines or larger chunks of text
  • fmt.Scan, fmt.Scanln, or fmt.Scanf for scanning space-separated values into variables

If you want behavior similar to C#'s Console.ReadLine(), the closest match is usually reading from os.Stdin with bufio.Reader.

Why this matters

Console input is useful in many kinds of programs:

  • small command-line tools
  • coding challenge programs
  • interactive scripts
  • configuration prompts
  • debugging utilities

A key detail in Go is that some input functions expect pointers to variables. If you pass a normal value instead of a pointer, the function cannot store the result in your variable.

For example, this is wrong:

text2 := ""
fmt.Scanln(text2)

This passes the value of text2, not its address. Go needs the address so it can write the input into that variable.

Correct version:

text2 := ""
fmt.Scanln(&text2)

Also, fmt.Sscanln does not read from standard input. It scans data from a string you already have in memory. That is why it is not the right tool for reading from the terminal.

Mental Model

Think of standard input as a stream of characters flowing from the keyboard into your program.

  • os.Stdin is the pipe connected to the keyboard input
  • bufio.Reader is a helper that lets you collect characters until a stopping point, such as a newline
  • fmt.Scanln is more like a form parser: it reads typed values and places them into variables you provide

A simple analogy:

  • bufio.Reader = reading an entire sentence from a notebook line
  • fmt.Scanln = filling labeled boxes with typed values

If you want the whole line exactly as the user typed it, use a reader. If you want individual values like a name, age, or number, scanning functions can be simpler.

Take Quiz

Syntax and Examples

Read a full line with bufio.Reader

package main

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

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

    fmt.Print("Enter text: ")
    text, err := reader.ReadString('\n')
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    text = strings.TrimSpace(text)
    fmt.Println("You entered:", text)
}

Explanation

  • bufio.NewReader(os.Stdin) creates a reader connected to keyboard input.
  • ReadString('\n') keeps reading until the user presses Enter.
  • The returned text usually includes the newline, so strings.TrimSpace() is often used to remove it.

Read a single word with fmt.Scanln

package main

import "fmt"

func main() {
    var name string

    fmt.Print("Enter your name: ")
    _, err := fmt.Scanln(&name)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Hello,", name)
}

Explanation

  • fmt.Scanln(&name) stores input in name
  • &name is required because the function needs a pointer
  • Scanln is best for simple values, not full sentences with spaces

Modern full-line option with bufio.Scanner

package main

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

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

    fmt.Print("Enter text: ")
    if scanner.Scan() {
        text := scanner.Text()
        fmt.Println("You entered:", text)
    }

    if err := scanner.Err(); err != nil {
        fmt.Println("Error:", err)
    }
}

This is often the cleanest way to read one line at a time.

Step by Step Execution

Consider this example:

package main

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

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

    fmt.Print("Enter text: ")
    input, err := reader.ReadString('\n')
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    input = strings.TrimSpace(input)
    fmt.Println("Final value:", input)
}

What happens step by step

  1. reader := bufio.NewReader(os.Stdin)

    • A reader is created that listens to terminal input.
  2. fmt.Print("Enter text: ")

    • The prompt is shown without adding a newline.
  3. input, err := reader.ReadString('\n')

    • The program pauses and waits.
    • The user types something like hello world and presses Enter.
    • The value returned is usually "hello world\n".
  4. if err != nil { ... }

    • If reading fails, the program prints the error and stops.
  5. input = strings.TrimSpace(input)

    • Removes the trailing newline and any surrounding whitespace.
    • Now the value is "hello world".
  6. fmt.Println("Final value:", input)

    • Prints the cleaned result.

Why your fmt.Scanln(text2) version fails

text2 := ""
fmt.Scanln(text2)

Step by step:

  1. text2 is an empty string.
  2. fmt.Scanln(text2) receives a copy of that empty string.
  3. The function cannot modify the original variable.
  4. As a result, text2 stays empty.

Correct version:

text2 := ""
fmt.Scanln(&text2)

Now Go receives the address of text2 and can store the input there.

Real World Use Cases

Reading from standard input is common in practical Go programs.

Command-line utilities

A script may ask for a filename, search term, or confirmation:

fmt.Print("Enter filename: ")

Coding challenge platforms

Many competitive programming tasks read data from standard input instead of from files or prompts.

Setup scripts

A tool might ask for environment values:

  • database host
  • port number
  • API key label

Interactive admin tools

Terminal apps often request commands like:

  • create
  • delete
  • list

Data entry programs

You may ask the user to enter names, quantities, or notes, then process those values inside the program.

Take Quiz

Real Codebase Usage

In real Go codebases, developers usually choose the input tool based on the shape of the data.

Common patterns

Full-line input for free-form text

Used for:

  • search queries
  • comments
  • prompts with spaces
  • file paths

Typical choice:

reader := bufio.NewReader(os.Stdin)
line, err := reader.ReadString('\n')

Token-based input for structured values

Used for:

  • menu choices
  • numbers
  • short identifiers

Typical choice:

var choice int
fmt.Scanln(&choice)

Validation after input

Developers rarely trust raw input directly.

if strings.TrimSpace(name) == "" {
    fmt.Println("Name cannot be empty")
    return
}

Early returns on input errors

A very common Go style is:

input, err := reader.ReadString('\n')
if err != nil {
    return
}

This keeps code simple and readable.

Reusable helper functions

In larger codebases, input logic is often wrapped in functions:

func readLine() (string, error) {
    reader := bufio.NewReader(os.Stdin)
    return reader.ReadString('\n')
}

This avoids repeating the same setup code everywhere.

Common Mistakes

1. Passing a value instead of a pointer

Broken code:

var name string
fmt.Scanln(name)

Problem:

  • Scanln cannot write into name because it needs a pointer.

Correct code:

var name string
fmt.Scanln(&name)

2. Using fmt.Sscanln for terminal input

Broken code:

var name string
fmt.Sscanln("%v", name)

Problem:

  • Sscanln scans from a string, not from standard input.
  • It is the wrong function for keyboard input.

Correct approach:

fmt.Scanln(&name)

or

reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')

3. Ignoring errors

Broken code:

text, _ := reader.ReadString('\n')

Problem:

  • If reading fails, you do not know why.

Better:

text, err := reader.ReadString('\n')
if err != nil {
    fmt.Println("Error:", err)
    return
}

4. Expecting Scanln to read full sentences easily

Broken expectation:

var text string
fmt.Scanln(&text)

If the user types hello world, Scanln is not ideal for preserving the entire line as free-form text.

Use:

reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')

5. Forgetting to remove the newline

With ReadString('\n'), the returned string often ends with \n.

Fix:

text = strings.TrimSpace(text)

6. Mixing multiple input methods carelessly

If you mix Scanln, Scan, and buffered readers without understanding how they consume input, leftover newlines can cause confusing results.

A good beginner rule:

  • Prefer one style consistently in a small program
  • Use bufio.Reader or bufio.Scanner for line-based input
  • Use fmt.Scanln for simple token-based input

Comparisons

ApproachBest forReads spaces?Needs pointer?Notes
bufio.Reader.ReadString('\n')Full linesYesNoClosest to Console.ReadLine()
bufio.ScannerLine-by-line inputYesNoSimple and clean for basic line reading
fmt.ScanSpace-separated valuesNoYesReads tokens until whitespace
fmt.ScanlnValues on one lineLimitedYesStops at newline; good for simple input
fmt.ScanfFormatted inputDepends on formatYesUseful when input format is strict
fmt.SscanlnScanning from a string variableNot for stdinYesDoes not read from keyboard

bufio.Reader vs fmt.Scanln

  • Use bufio.Reader when you want the entire line
  • Use fmt.Scanln when you want one or more simple values

bufio.Reader vs bufio.Scanner

  • Reader gives more control
  • Scanner is often easier for beginners when reading line by line

If your goal is a Go version of Console.ReadLine(), bufio.Reader or bufio.Scanner is usually the best match.

Take Quiz

Cheat Sheet

Full line input

reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')

Remove newline:

text = strings.TrimSpace(text)

Single value input

var name string
_, err := fmt.Scanln(&name)

Important rule

Scanning functions need pointers:

fmt.Scanln(&value) // correct
fmt.Scanln(value)  // wrong

Useful imports

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

Best tool by situation

  • Full sentence or line: bufio.Reader or bufio.Scanner
  • Single word or number: fmt.Scanln
  • Parse from an existing string: fmt.Sscanln

Common edge cases

  • ReadString('\n') includes the newline
  • Scanln is not ideal for long text with spaces
  • Always check err when reading input
  • Mixing input styles can leave extra newlines behind

FAQ

What is the Go equivalent of Console.ReadLine()?

The closest equivalent is usually reading from os.Stdin using bufio.Reader or bufio.Scanner.

Why does fmt.Scanln(text2) not work?

Because Scanln needs a pointer. Use fmt.Scanln(&text2) instead.

Should I use bufio.Reader or bufio.Scanner?

Use bufio.Scanner for simple line-by-line input. Use bufio.Reader when you want more control.

Does fmt.Sscanln read from the keyboard?

No. It reads from a string already in memory, not from standard input.

Why does my input contain a newline character?

Functions like ReadString('\n') include the newline in the returned string. Use strings.TrimSpace() to remove it.

Can fmt.Scanln read text with spaces?

It is not the best choice for free-form full-line input. For text with spaces, use bufio.Reader or bufio.Scanner.

Why does the program seem to exit immediately?

This often happens because the input function is used incorrectly, such as passing a non-pointer or using the wrong scan function.

Take Quiz

Mini Project

Description

Build a small Go program that asks the user for their name and favorite programming language, then prints a friendly summary. This demonstrates how to read full-line console input safely and cleanly.

Goal

Create a console app that reads two lines of user input and displays them back without trailing newline characters.

Requirements

Requirement 1 Requirement 2 Requirement 3

Take Quiz

Keep learning

Related questions

View more