概述
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) // 写入一个字符串
通过这些方法,开发者可以更灵活、高效地处理数据的读写操作。