Golang使用bufio缓冲读取文件示例
在Golang中,当我们需要逐行读取文件内容时,使用bufio包提供的缓冲读取功能可以显著提高性能。本文将介绍如何使用bufio.Scanner和bufio.Reader来实现高效的文件读取。
为什么使用缓冲读取?
直接读取文件会频繁进行I/O操作,效率较低。缓冲读取通过一次性读取大量数据到内存缓冲区,然后逐步从缓冲区获取数据,大大减少了I/O次数,提高了读取效率。
使用bufio.Scanner逐行读取文件
bufio.Scanner是最简单的逐行读取方式,适合大多数场景。
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
// 打开文件
file, err := os.Open("example.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// 创建Scanner
scanner := bufio.NewScanner(file)
// 逐行读取
lineNum := 1
for scanner.Scan() {
fmt.Printf("第%d行: %s\n", lineNum, scanner.Text())
lineNum++
}
// 检查是否有错误发生
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}代码说明
使用os.Open()打开文件,记得用defer关闭文件
bufio.NewScanner()创建Scanner对象
scanner.Scan()方法移动到下一行,返回bool值表示是否还有数据
scanner.Text()获取当前行的文本内容
scanner.Err()检查读取过程中是否发生错误
使用bufio.Reader读取固定大小的数据
如果需要更精细的控制,可以使用bufio.Reader按字节或指定分隔符读取。
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
// 打开文件
file, err := os.Open("example.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// 创建Reader,缓冲区大小为16KB
reader := bufio.NewReaderSize(file, 16384)
// 读取数据
buffer := make([]byte, 1024) // 每次读取1KB
var totalBytes int
for {
n, err := reader.Read(buffer)
if n > 0 {
totalBytes += n
fmt.Printf("读取了%d字节: %s\n", n, string(buffer[:n]))
}
if err != nil {
break
}
}
fmt.Printf("总共读取了%d字节\n", totalBytes)
}代码说明
bufio.NewReaderSize()可以指定缓冲区大小
reader.Read()方法读取数据到字节切片中
返回读取的字节数和可能的错误
当err不为nil时,表示已到达文件末尾或发生错误
自定义分割函数
Scanner默认以换行符分割,但我们可以自定义分割函数来满足特殊需求。
package main
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"strings"
)
// 自定义分割函数:按逗号分割
func commaSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, ','); i >= 0 {
return i + 1, data[0:i], nil
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
}
func main() {
// 准备测试数据
content := "apple,banana,orange,grape"
reader := strings.NewReader(content)
scanner := bufio.NewScanner(reader)
scanner.Split(commaSplit)
for scanner.Scan() {
fmt.Printf("单词: %s\n", scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}代码说明
自定义分割函数签名必须为func([]byte, bool) (int, []byte, error)
advance参数表示读取了多少字节
token参数是分割出的数据
atEOF表示是否已到达输入末尾
性能考虑
对于大文件,适当增大缓冲区可以提高性能
如果只是简单逐行读取,优先使用Scanner
需要精细控制时使用Reader
记得及时关闭文件释放资源
通过合理使用bufio包的缓冲读取功能,可以在处理文件I/O时获得更好的性能表现。