在 Go 语言中,文件读写和数据流处理是非常常见的操作,例如日志处理、文本解析、网络通信等。如果直接使用
os或io进行读写,每一次操作都可能触发系统调用,这在高频 IO 场景下性能开销较大。
为了解决这个问题,Go 提供了 bufio 包,它通过**缓冲机制(buffer)**来减少 IO 次数,从而显著提升性能。简单来说,bufio 的核心作用就是:把多次小的 IO 操作合并成少量大的 IO 操作。
bufio 主要提供三种核心类型:
Reader Writer Scanner
分别用于读取、写入和扫描数据。
bufio.Reader:高效读取数据
bufio.Reader 是对 io.Reader 的封装,它会在内部维护一个缓冲区,从底层数据源中批量读取数据,从而减少系统调用次数。
最常见的使用方式是逐行读取文件,例如处理日志文件或文本数据。
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, _ := os.Open("test.txt")
defer file.Close()
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
fmt.Print(line)
}
}
这个例子中,ReadString('\n') 会一直读取直到遇到换行符,非常适合按行处理文本。
除了 ReadString,还可以使用 ReadBytes:
go
line, _ := reader.ReadBytes('\n')
两者区别不大,只是返回类型不同,一个是 string,一个是 []byte。
如果需要更细粒度控制,可以使用 Read() 方法:
go
buf := make([]byte, 1024)
n, _ := reader.Read(buf)
fmt.Println(string(buf[:n]))
这种方式适合读取二进制数据或大文件。
bufio.Writer:高效写入数据
与 Reader 相对应,bufio.Writer 用于缓冲写入数据。它会先把数据写入内存缓冲区,等到缓冲区满或者手动刷新时,再一次性写入底层 IO。
go
package main
import (
"bufio"
"os"
)
func main() {
file, _ := os.Create("output.txt")
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString("Hello ")
writer.WriteString("Go")
writer.Flush()
}
需要特别注意:必须调用 Flush(),否则数据可能仍然停留在缓冲区中,没有真正写入文件。
在高频写入场景(例如日志系统)中,bufio.Writer 可以显著提升性能。
bufio.Scanner:简洁文本扫描工具
如果只是做文本解析,bufio.Scanner 是更简单、更推荐的工具。它提供了类似"逐行扫描"的能力,代码更简洁。
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, _ := os.Open("test.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
这种写法非常适合:
日志分析 CSV 解析 配置文件读取
默认情况下,Scanner 按行分割数据。
自定义分割规则
Scanner 支持自定义分割方式,例如按单词分割:
go
scanner.Split(bufio.ScanWords)
示例:
go
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
还可以使用:
text
bufio.ScanLines
bufio.ScanWords
bufio.ScanRunes
甚至可以自定义分割函数,适用于复杂协议解析。
处理大文件的注意事项
bufio.Scanner 默认有大小限制(64K),如果一行数据太长会报错:
text
token too long
解决方法:
go
scanner.Buffer(make([]byte, 1024), 1024*1024)
这样可以把最大读取限制提高到 1MB 或更大。
如果数据非常大,建议使用 bufio.Reader。
常见实战场景
在实际开发中,bufio 使用非常广泛。例如:
场景一:日志文件分析
go
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "ERROR") {
fmt.Println(line)
}
}
用于筛选错误日志。
场景二:构建高性能写入
go
writer := bufio.NewWriter(file)
for i := 0; i < 100000; i++ {
writer.WriteString("log line\n")
}
writer.Flush()
适合批量写入日志。
场景三:网络数据读取
go
conn, _ := net.Dial("tcp", "example.com:80")
reader := bufio.NewReader(conn)
line, _ := reader.ReadString('\n')
fmt.Println(line)
适用于 HTTP 或 TCP 协议解析。
bufio 与 io 区别
Go 中有两个常见 IO 包:
lua
io
bufio
区别如下:
| 包 | 特点 |
|---|---|
| io | 直接读写,无缓冲 |
| bufio | 带缓冲,提高性能 |
总结:
- 小数据或简单场景可以直接用
io - 高频 IO 或大数据处理建议用
bufio
使用建议
在实际开发中可以遵循以下经验:
读取文本文件优先使用 Scanner 大文件或复杂读取使用 Reader 高频写入使用 Writer 写入完成一定要 Flush
这样可以保证:
性能更高 代码更简洁 更稳定可靠
总结
bufio 是 Go 标准库中用于 高性能 IO 操作 的重要工具,它通过缓冲机制减少系统调用,从而显著提升读写效率。
核心组件包括:
Reader:高效读取 Writer:高效写入 Scanner:文本扫描
适用场景包括:
日志处理 文件读写 网络通信 数据解析
在构建日志系统、文件处理工具、爬虫程序、网络服务等项目时,bufio 几乎是必备工具。
熟练掌握 bufio,可以让你的 Go 程序在 IO 性能和代码结构上都有明显提升。