概述
bufio 包是对 io 包的封装,提供了数据缓冲功能,能够在处理大块数据读写时减少开销。在 bufio 中,各个组件内部都维护了一个缓冲区。数据的读写操作首先通过缓冲区进行,只有当缓冲区没有数据时,才会从数据源获取数据并更新缓冲区。
Reader
可以通过 NewReader 函数创建 bufio.Reader 对象。该函数接收一个 io.Reader 作为参数,这意味着 bufio.Reader 需要绑定到某个 io.Reader 上才能使用。函数声明如下:
func NewReader(rd io.Reader) *Reader
func NewReaderSize(rd io.Reader, size int) *Reader // 可以配置缓冲区的大小
相比于 io.Reader,bufio.Reader 提供了更多实用的方法,以更有效地读取数据。以下是几个基础方法,它们能够对 Reader 进行细粒度的操作:
- Read:读取指定长度的字节数据。
- Discard:丢弃接下来指定长度的字节数据。
- Peek:获取缓冲区内接下来的字节数据,但不移动指针。
- Reset:清空整个缓冲区并绑定新的 io.Reader。
具体方法声明如下:
func (b *Reader) Read(p []byte) (n int, err error)
func (b *Reader) Discard(n int) (discarded int, err error)
func (b *Reader) Peek(n int) ([]byte, error)
func (b *Reader) Reset(r io.Reader)
除了基础操作,bufio.Reader 还提供了更多高级抽象层次的方法,用于简化数据的结构化读取。主要包括以下几个方法:
- ReadByte:读取一个字节。
- ReadRune:读取一个 UTF-8 字符。
- ReadLine:按行读取数据(由 \n分隔)。
- ReadBytes:读取一个字节列表,直到遇到指定分隔符。
- ReadString:读取一个字符串,直到遇到指定分隔符。
其中前三个函数无参数,直接从缓冲区读取满足需求的数据。后两个函数接收一个 delim 参数,用于分隔数据,持续读取数据直到遇到该分隔符,然后返回读取的数据。具体方法声明如下:
func (b *Reader) ReadByte() (byte, error)
func (b *Reader) ReadRune() (r rune, size int, err error)
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
func (b *Reader) ReadBytes(delim byte) ([]byte, error)
func (b *Reader) ReadString(delim byte) (string, error)
下面是一个使用 ReadString 方法读取以空格分隔的字符串的示例:
package main
import (
	"bufio"
	"fmt"
	"strings"
)
func main() {
	r := strings.NewReader("hello world !")
	reader := bufio.NewReader(r)
	for {
		str, err := reader.ReadString(byte(' '))
		fmt.Println(str)
		if err != nil {
			return
		}
	}
}
Scanner
实际使用中,更推荐使用 Scanner 进行数据读取,而非直接使用 Reader 类。Scanner 可以通过 splitFunc 将输入数据拆分为多个 token,然后依次进行读取。
与 Reader 类似,Scanner 需要绑定到某个 io.Reader 上,通过 NewScanner 创建,函数声明如下:
func NewScanner(r io.Reader) *Scanner
使用 Scanner 之前,需要设置 splitFunc(默认为 ScanLines)。splitFunc 用于将输入数据拆分为多个 token。bufio 模块提供了几个默认的 splitFunc,能够满足大部分场景需求,包括:
- ScanBytes:按字节拆分。
- ScanLines:按行(\n)拆分。
- ScanRunes:按 UTF-8 字符拆分。
- ScanWords:按单词(空格)拆分。
通过 Scanner 的 Split 方法可以指定 splitFunc。使用方法如下:
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
此外,用户还可以定义自己的 splitFunc,其函数声明如下:
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
完成 Scanner 初始化后,通过 Scan 方法可以依次读取每个 token。读取成功返回 true,可通过 Text 和 Bytes 方法获取 token 值,Text 返回字符串,Bytes 返回字节数组。方法声明如下:
func (s *Scanner) Scan() bool
func (s *Scanner) Text() string
func (s *Scanner) Bytes() []byte
以下示例使用 Scanner 对上面的示例进行了重现,可以看到与 Reader 相比,Scanner 使用更加便捷:
package main
import (
	"bufio"
	"strings"
	"fmt"
)
func main() {
	scanner := bufio.NewScanner(strings.NewReader("hello world !"))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
}
Writer
与 Reader 类似,bufio.Writer 也提供了一组方法用于数据写入。基础方法包括:
func (b *Writer) Write(p []byte) (nn int, err error) // 写入字节数据
func (b *Writer) Reset(w io.Writer) // 重置当前缓冲区
func (b *Writer) Flush() error // 刷新缓冲区,将数据写入输出
此外,Writer 还提供了以下便捷方法:
func (b *Writer) WriteByte(c byte) error  // 写入一个字节
func (b *Writer) WriteRune(r rune) (size int, err error) // 写入一个字符
func (b *Writer) WriteString(s string) (int, error) // 写入一个字符串
通过这些方法,开发者可以更灵活、高效地处理数据的读写操作。