bufio 包 - Go 缓冲 I/O 详解
bufio 包提供了带缓冲的 I/O 操作,通过减少系统调用次数来显著提升 I/O 性能。理解 bufio 是高效处理文件、网络等 I/O 操作的关键。
📌 核心概念
📖
Reader
缓冲读取
ReadString
✍️
Writer
缓冲写入
WriteString
🔍
Scanner
文本扫描
Scan()
⚡
性能提升
减少系统调用
10x 性能
bufio.Reader 缓冲读取
📝 Reader 基础使用
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// 从字符串读取
reader := bufio.NewReader(strings.NewReader("Hello, World!\n"))
// 读取到分隔符
line, _ := reader.ReadString('\n')
fmt.Println(line)
// 读取单个字节
b, _ := reader.ReadByte()
fmt.Printf("Byte: %c\n", b)
// 读取到指定分隔符(丢弃)
reader.ReadBytes('\n')
// 读取一行(不包括\n)
reader2 := bufio.NewReader(strings.NewReader("Line 1\nLine 2\n"))
line2, _ := reader2.ReadLine()
fmt.Printf("Line: %s\n", line2)
// 从文件读取
file, _ := os.Open("test.txt")
defer file.Close()
bufReader := bufio.NewReader(file)
content, _ := bufReader.ReadString('\n')
fmt.Println(content)
}
💡 Reader 要点
- 默认缓冲: 默认缓冲区大小为 4KB
- 自定义大小: NewReaderSize 可指定大小
- Peek: 预览数据而不消耗
- Discard: 跳过指定字节数
bufio.Writer 缓冲写入
📝 Writer 基础使用
package main
import (
"bufio"
"bytes"
"fmt"
"os"
)
func main() {
// 写入 bytes.Buffer
var buf bytes.Buffer
writer := bufio.NewWriter(&buf)
// 缓冲写入
writer.WriteString("Hello, ")
writer.WriteString("World!\n")
// 必须刷新才能看到数据
writer.Flush()
fmt.Println(buf.String())
// 写入文件
file, _ := os.Create("output.txt")
defer file.Close()
bufWriter := bufio.NewWriter(file)
bufWriter.WriteString("File content\n")
bufWriter.Flush() // 必须刷新
// 可用字节数
fmt.Printf("Available: %d\n", bufWriter.Available())
// 缓冲大小
fmt.Printf("Size: %d\n", bufWriter.BufferSize())
}
⚠️ Writer 注意事项
- 必须 Flush: 数据在缓冲区,必须调用 Flush
- 错误处理: Flush 可能返回错误
- 资源清理: 使用 defer 确保 Flush 被调用
bufio.Scanner 文本扫描
📝 Scanner 逐行读取
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// 逐行读取字符串
text := "Line 1\nLine 2\nLine 3\n"
scanner := bufio.NewScanner(strings.NewReader(text))
for scanner.Scan() {
line := scanner.Text()
fmt.Println(line)
}
// 检查错误
if err := scanner.Err(); err != nil {
fmt.Printf("Scan error: %v\n", err)
}
// 从文件逐行读取
file, _ := os.Open("test.txt")
defer file.Close()
scanner2 := bufio.NewScanner(file)
lineNum := 0
for scanner2.Scan() {
lineNum++
fmt.Printf("Line %d: %s\n", lineNum, scanner2.Text())
}
}
📝 自定义分割函数
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// 按单词扫描
text := "Hello world from Go"
scanner := bufio.NewScanner(strings.NewReader(text))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 按 rune 扫描
scanner2 := bufio.NewScanner(strings.NewReader("ABC"))
scanner2.Split(bufio.ScanRunes)
for scanner2.Scan() {
fmt.Printf("Rune: %s\n", scanner2.Text())
}
// 自定义分割函数:按逗号分割
commaSplit := func(data []byte, atEOF bool) (int, []byte, error) {
for i := 0; i < len(data); i++ {
if data[i] == ',' {
return i + 1, data[:i], nil
}
}
return 0, nil, nil
}
text2 := "apple,banana,orange,grape"
scanner3 := bufio.NewScanner(strings.NewReader(text2))
scanner3.Split(commaSplit)
for scanner3.Scan() {
fmt.Println(scanner3.Text())
}
}
性能对比
📊 bufio vs 直接 I/O
package main
import (
"bufio"
"fmt"
"io"
"os"
"time"
)
// 直接读取(无缓冲)
func readWithoutBuffer(filename string) error {
file, _ := os.Open(filename)
defer file.Close()
buf := make([]byte, 1) // 每次 1 字节
for {
_, err := file.Read(buf)
if err == io.EOF {
break
}
}
return nil
}
// 缓冲读取
func readWithBuffer(filename string) error {
file, _ := os.Open(filename)
defer file.Close()
reader := bufio.NewReader(file)
buf := make([]byte, 1)
for {
_, err := reader.Read(buf)
if err == io.EOF {
break
}
}
return nil
}
func main() {
// 创建测试文件
file, _ := os.Create("test.dat")
for i := 0; i < 100000; i++ {
file.Write([]byte("0123456789\n"))
}
file.Close()
// 测试无缓冲
start := time.Now()
readWithoutBuffer("test.dat")
fmt.Printf("Without buffer: %v\n", time.Since(start))
// 测试有缓冲
start = time.Now()
readWithBuffer("test.dat")
fmt.Printf("With buffer: %v\n", time.Since(start))
// 结果:缓冲读取快 100-1000 倍
}
实用模式
📝 文件处理模式
package main
import (
"bufio"
"fmt"
"os"
)
// 模式 1: 大文件逐行处理
func processLargeFile(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
// 处理每一行...
fmt.Println(line)
}
return scanner.Err()
}
// 模式 2: 高效写入文件
func writeLargeFile(filename string, lines []string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
writer := bufio.NewWriter(file)
for _, line := range lines {
writer.WriteString(line + "\n")
}
return writer.Flush()
}
// 模式 3: 交互式输入
func readUserInput() string {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter something: ")
input, _ := reader.ReadString('\n')
return input[:len(input)-1] // 去掉换行符
}
func main() {
processLargeFile("large.txt")
writeLargeFile("output.txt", []string{"Line 1", "Line 2"})
input := readUserInput()
fmt.Printf("You entered: %s\n", input)
}
最佳实践
✅ bufio 使用建议
- 大文件处理: 使用 Scanner 逐行读取
- 高效写入: 使用 Writer 缓冲写入
- 必须 Flush: Writer 用后必须调用 Flush
- 错误检查: Scanner 用后检查 Err()
- 自定义缓冲: 大记录使用 NewReaderSize
🚨 常见陷阱
- 忘记 Flush: Writer 数据丢失
- Scanner 限制: 默认最大行 64KB
- 并发使用: Reader/Writer 不是并发安全的
- 资源泄漏: 文件用后必须 Close