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.
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.
sequenceDiagramUser->>fmt: Scan / Scanlnfmt->>App: parsed inputApp->>App: ValidateApp->>fmt: Println result
Step-by-step explanation
- fmt.Printf writes formatted text to stdout with verbs matching argument types.
- Scanner.Scan() reads until newline; Scanner.Text() returns the line without \n.
- Fprintf(w, format, args) writes to any io.Writer — files, buffers, HTTP responses.
- Sprintf returns formatted string without printing.
- Builder.WriteString and Builder.String() build large strings in O(n) total allocation.
- 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:
package mainimport ("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 intif _, err := fmt.Fscan(reader, &qty); err != nil {fmt.Fprintln(os.Stderr, "invalid quantity:", err)os.Exit(1)}var b strings.Builderfmt.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) erroraccepts 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.Stdinandos.Stdoutare pre-built*os.Filevalues satisfyingio.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?
Q2BeginnerPurpose of strings.Builder?
Q3Intermediatefmt.Printf %v vs %+v vs %#v?
Q4IntermediateRead large file without loading all into memory?
Q5AdvancedDesign CLI that works in CI and interactively.
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.