Go (Golang) Tutorial 0/45 lessons ~6 min read Lesson 6

    Input and Output

    Go's fmt package handles formatted I/O; bufio adds buffered reading for performance; os provides stdin, stdout, and stderr file descriptors.

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Go's fmt package handles formatted I/O; bufio adds buffered reading for performance; os provides stdin, stdout, and stderr file descriptors. Production CLIs and services use these packages daily — from logging user prompts to streaming large files.

    Understanding Printf verbs (%s, %d, %v, %+v, %#v), Fprintln to specific writers, and Scanner for line-by-line input builds the foundation for parsing config files, reading HTTP bodies, and building interactive tools.

    This lesson also introduces strings.Builder for efficient string concatenation — critical when building SQL queries, log messages, or HTML templates without allocation churn.

    The story

    A Kubernetes admission webhook reads a JSON patch payload from stdin, validates it, and writes an approval or rejection message to stdout. In tests, the same logic accepts an io.Reader and io.Writer so QA can inject fake input without standing up an API server — a pattern used daily in Google's internal CI pipelines.

    Separating I/O from business logic lets you unit-test validation rules that protect production clusters from misconfigured deployments.

    Understanding the topic

    Key concepts

    • fmt.Printf, Println, Sprintf format output with verb placeholders.
    • fmt.Scan, Scanln, Scanf read formatted input from stdin.
    • bufio.NewScanner reads line-by-line; bufio.NewReader supports Peek and ReadString.
    • os.Stdin, os.Stdout, os.Stderr are *os.File implementing io.Reader/Writer.
    • strings.Builder accumulates strings efficiently without repeated allocations.
    • io.Copy streams data between Reader and Writer — used in HTTP and file ops.
    text
    sequenceDiagram
    User->>fmt: Scan / Scanln
    fmt->>App: parsed input
    App->>App: Validate
    App->>fmt: Println result

    Step-by-step explanation

    1. fmt.Printf writes formatted text to stdout with verbs matching argument types.
    2. Scanner.Scan() reads until newline; Scanner.Text() returns the line without \n.
    3. Fprintf(w, format, args) writes to any io.Writer — files, buffers, HTTP responses.
    4. Sprintf returns formatted string without printing.
    5. Builder.WriteString and Builder.String() build large strings in O(n) total allocation.
    6. io.Copy(dst, src) buffers internally and handles large streams.

    Practical code example

    Interactive CLI reading user input and building formatted output with strings.Builder:

    go
    package main
    import (
    "bufio"
    "fmt"
    "os"
    "strings"
    )
    func main() {
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("Enter product name: ")
    name, _ := reader.ReadString('\n')
    name = strings.TrimSpace(name)
    fmt.Print("Enter quantity: ")
    var qty int
    if _, err := fmt.Fscan(reader, &qty); err != nil {
    fmt.Fprintln(os.Stderr, "invalid quantity:", err)
    os.Exit(1)
    }
    var b strings.Builder
    fmt.Fprintf(&b, "Order Summary\n Product: %s\n Qty: %d\n", name, qty)
    fmt.Print(b.String())
    }

    Output

    Enter deployment name:
    approved: payments-api-v2

    Line-by-line code explanation

    • func Process(r io.Reader, w io.Writer) error accepts abstract streams for testability.
    • scanner := bufio.NewScanner(r) wraps a reader for convenient line-by-line input.
    • scanner.Scan() advances to the next line — returns false at EOF.
    • line := scanner.Text() returns the current line without the trailing newline.
    • scanner.Err() checks for read errors after the loop completes.
    • fmt.Fprintf(w, "approved: %s\n", line) writes formatted output to any writer.
    • os.Stdin and os.Stdout are pre-built *os.File values satisfying io.Reader/io.Writer.
    • bytes.NewBufferString("test") creates an in-memory reader for unit tests.

    Key takeaway: Always check errors from Fscan in production. strings.TrimSpace removes trailing newline from ReadString. Prefer Scanner for simple line input.

    Real-world use

    Where you'll use this in production

    • Interactive CLI tools for database migrations and admin tasks.
    • Parsing log files line-by-line with bufio.Scanner.
    • Streaming HTTP response bodies with io.Copy.
    • Building dynamic SQL or JSON with strings.Builder in template-free code.

    Best practices

    • Write errors to os.Stderr; data to os.Stdout for pipeline compatibility.
    • Use fmt.Errorf or log package instead of fmt for error messages in servers.
    • Prefer Scanner over ReadString for line-based input — handles long lines.
    • Use strings.Builder for concatenation in loops — never += on strings in hot paths.
    • Check all Scan/Read errors; don't ignore with _ in production.

    Common mistakes

    • Using fmt.Scan for lines with spaces — stops at first space.
    • Forgetting TrimSpace after ReadString — newline breaks comparisons.
    • String concatenation in loops causing O(n²) allocations.
    • Printing sensitive data (passwords, tokens) to stdout in logs.
    • Not flushing buffered writers before exit.

    Advanced interview questions

    Q1BeginnerDifference between Print and Println?
    Print writes args with default formatting; Println adds spaces between args and trailing newline.
    Q2BeginnerPurpose of strings.Builder?
    Efficient mutable string buffer avoiding repeated allocations during concatenation.
    Q3Intermediatefmt.Printf %v vs %+v vs %#v?
    %v default format; %+v struct with field names; %#v Go-syntax representation.
    Q4IntermediateRead large file without loading all into memory?
    bufio.Scanner line-by-line or io.Copy to writer; os.Open returns io.Reader.
    Q5AdvancedDesign CLI that works in CI and interactively.
    Detect terminal: fileInfo, _ := os.Stdin.Stat(); if (fileInfo.Mode() & os.ModeCharDevice) == 0 { read from args/env } else { prompt }.

    Summary

    fmt handles formatted I/O; bufio adds buffering for performance. os.Stdin/Stdout/Stderr separate input, output, and errors. strings.Builder avoids allocation-heavy string concatenation. Always validate and trim user input; check scan errors. Next lesson: if, else, and switch for control flow.

    Ready to mark this lesson complete?Track your journey across the entire course.