bufio.Scanner and bufio.Reader
The `bufio` package provides buffered I/O operations, with `Scanner` for tokenizing input (e.g., line by line) and `Reader` for more granular buffered reading.
scanner := bufio.NewScanner(r io.Reader)
reader := bufio.NewReader(r io.Reader)This static page keeps the syntax and examples indexed for search, while the coding app handles interactive exploration and saved references.
What it does
Overview
The `bufio` package provides buffered I/O operations, with `Scanner` for tokenizing input (e.g., line by line) and `Reader` for more granular buffered reading.
The `bufio` package enhances I/O performance by adding buffering capabilities to `io.Reader` and `io.Writer` interfaces. `bufio.Scanner` is designed for simple, efficient reading of input, typically line by line or word by word, making it ideal for parsing text files or standard input. It handles buffering internally and provides an iterator-like interface (`Scan`, `Text`, `Bytes`) that simplifies common parsing tasks. The `Scan` method advances the scanner to the next token, `Text` returns the current token as a string, and `Bytes` returns it as a byte slice. `bufio.Reader`, on the other hand, offers more control over buffered input, allowing operations like `ReadByte`, `Peek`, `ReadString`, and `ReadLine`. It's suitable when you need to read specific amounts of data, look ahead in the stream, or handle more complex parsing logic than `Scanner` provides out-of-the-box. Both types improve performance by reducing the number of underlying system calls to the `io.Reader`.
Time complexity for reading operations generally depends on the underlying `io.Reader` and the size of the buffer. For `Scanner`, `Scan` is typically O(N) where N is the length of the token, but amortized O(1) for small tokens due to buffering. `Reader` methods like `ReadString` can be O(N) where N is the length of the string read. Edge cases include handling large lines that exceed the default buffer size (Scanner might return an error or truncate), and ensuring proper error handling after `Scan` or `Read` calls. Best practices include always checking the error returned by `Scan` or `Read` methods, and for `Scanner`, checking `scanner.Err()` after the loop to catch any errors that occurred during scanning.
Quick reference
Syntax
scanner := bufio.NewScanner(r io.Reader)
reader := bufio.NewReader(r io.Reader)
Inputs
Parameters
See it in practice
Examples
Reading a file line by line with bufio.Scanner
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
lineNum := 1
for scanner.Scan() {
fmt.Printf("Line %d: %s\n", lineNum, scanner.Text())
lineNum++
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading file:", err)
}
}Line 1: Hello, Go! Line 2: This is a test file. Line 3: End of content.
This example demonstrates how to use `bufio.Scanner` to read an `os.File` line by line. The `scanner.Scan()` method returns `true` as long as there's a next token (line, by default) and no error occurs. `scanner.Text()` retrieves the current line.
Reading a file word by word with bufio.Scanner
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Printf("Word: %s\n", scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading file:", err)
}
}Word: Hello, Word: Go! Word: This Word: is Word: a Word: test Word: file. Word: End Word: of Word: content.
By calling `scanner.Split(bufio.ScanWords)`, the scanner is configured to tokenize the input by words instead of lines. This allows iterating through individual words in the file.
Reading until a delimiter with bufio.Reader
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
s := "apple,banana,cherry\nhello world"
r := strings.NewReader(s)
reader := bufio.NewReader(r)
line, err := reader.ReadString(',')
if err != nil {
fmt.Println("Error reading string:", err)
}
fmt.Printf("Read until comma: %q\n", line)
remaining, err := reader.ReadString('\n')
if err != nil {
fmt.Println("Error reading string:", err)
}
fmt.Printf("Read until newline: %q\n", remaining)
peeked, _ := reader.Peek(5)
fmt.Printf("Peeked 5 bytes: %q\n", string(peeked))
}Read until comma: "apple," Read until newline: "banana,cherry\n" Peeked 5 bytes: "hello"
`bufio.Reader` offers methods like `ReadString` to read data until a specific delimiter. `Peek` allows inspecting upcoming bytes without consuming them from the buffer, which is useful for look-ahead parsing.
Debug faster
Common Errors
Scanner Buffer Overflow
Cause: A single token (e.g., a very long line) exceeds the default buffer size (typically 64KB) of `bufio.Scanner`.
Fix: Increase the scanner's buffer size using `scanner.Buffer(buf []byte, max int)` or handle the error `bufio.ErrTooLong`.
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
longLine := strings.Repeat("a", 70000) // Exceeds default 64KB
r := strings.NewReader(longLine)
scanner := bufio.NewScanner(r)
// Uncomment to fix:
// const maxCapacity = 1024 * 1024 // 1MB
// buf := make([]byte, maxCapacity)
// scanner.Buffer(buf, maxCapacity)
if !scanner.Scan() {
if err := scanner.Err(); err != nil {
fmt.Println("Error during scan:", err) // bufio.ErrTooLong
}
}
}Runtime support
Compatibility
Common questions
Frequently Asked Questions
The `bufio` package provides buffered I/O operations, with `Scanner` for tokenizing input (e.g., line by line) and `Reader` for more granular buffered reading.
r: The underlying reader to buffer.
Scanner Buffer Overflow: Increase the scanner's buffer size using `scanner.Buffer(buf []byte, max int)` or handle the error `bufio.ErrTooLong`.