bufio
bufio包提供了带缓冲的I/O操作,通过减少系统调用次数来提升读写效率。
1 常量
const (
// MaxScanTokenSize is the maximum size used to buffer a token
// unless the user provides an explicit buffer with [Scanner.Buffer].
// The actual maximum token size may be smaller as the buffer
// may need to include, for instance, a newline.
MaxScanTokenSize = 64 * 1024
)bufio 包中的 MaxScanTokenSize 常量定义了在用户未显式指定缓冲区时,Scanner 类型用于缓冲单个 token 的默认最大容量(64KB)。
核心作用
默认缓冲区限制:
当使用
bufio.NewScanner创建扫描器且未调用Buffer方法时,Scanner内部使用MaxScanTokenSize(64KB)作为缓冲区容量的上限。Token:指通过
Split函数(如ScanLines)分割出的数据块(如一行文本、一个单词)。
隐式分割限制:
若某次扫描的 token 超过缓冲区容量,
Scanner会返回bufio.ErrTooLong错误。实际容量更小:因缓冲区需预留空间存储分隔符(如换行符
\n),实际可存储的 token 数据略小于 64KB。
2 函数
2.1 ScanBytes:按字节分割
功能:将每个字节作为独立的token返回。
适用场景:需要逐字节处理二进制数据或字符流。
行为:
每次返回一个字节(
len(token) == 1)。不会返回空token(除非输入数据本身为空)。
示例:
2.2 ScanLines:按行分割
功能:返回每行文本(去除行尾的
\r\n或\n)。适用场景:处理文本文件、日志或网络协议中的行数据。
行为:
支持
\r\n(Windows)和\n(Unix)换行符。允许空行(返回空字符串
"")。文件末尾的非空行即使无换行符也会返回。
示例:
2.3 ScanRunes:按UTF-8字符分割
功能:将每个UTF-8编码的字符(rune)作为token返回。
适用场景:处理多语言文本或需要逐个字符分析的场景。
行为:
错误的UTF-8编码会被替换为
U+FFFD(Unicode替换字符)。等效于遍历字符串的
for range循环。
示例:
2.4 ScanWords:按单词分割
功能:返回被空格分隔的单词(去除前后空格)。
适用场景:解析空格分隔的数据(如CSV的简化版、文本统计)。
行为:
使用
unicode.IsSpace判断空格(包括\t、\n、\v等)。不会返回空字符串。
示例:
2.5 函数对比与选择指南
函数
分割单位
空token
典型场景
ScanBytes
单个字节
无
二进制处理、字符级分析
ScanLines
文本行
允许
日志处理、配置文件读取
ScanRunes
UTF-8字符
无
多语言文本处理、字形分析
ScanWords
单词
无
文本统计、简单数据解析
3 类型
3.1 Reader
bufio.Reader 为 io.Reader 提供了缓冲功能,减少直接访问底层数据源的次数,从而提升读取效率。
3.1.1 初始化
NewReader(rd io.Reader) *Reader 创建默认缓冲区大小(4096字节)的Reader。
NewReaderSize(rd io.Reader, size int) *Reader 自定义缓冲区大小,适用于需要优化内存或处理大块数据的场景。
3.1.2 基础读取
Read(p []byte) (n int, err error) 读取数据到p,可能未填满p。需检查n和err。 适用场景:通用数据读取,需手动处理部分读取情况。 注意:需配合io.ReadFull确保读取完整数据。
3.1.3 按分隔符读取
ReadBytes(delim byte) ([]byte, error) 读取直到遇到delim,返回包含分隔符的数据。 适用场景:按行(\n)或自定义分隔符(如,)解析数据。
ReadString(delim byte) (string, error) 功能同ReadBytes,但返回字符串,避免额外转换。
3.1.4 行读取
ReadLine() (line []byte, isPrefix bool, err error) 低级行读取方法,不包含行尾符。isPrefix表示行被截断。 注意:推荐使用ReadBytes('\n')或Scanner简化处理。
3.1.5 字符与字节处理
ReadByte() (byte, error) 读取单个字节,适用于二进制解析。
ReadRune() (r rune, size int, err error) 读取UTF-8字符,自动处理编码错误(返回U+FFFD)。
3.1.6 缓冲区操作
Peek(n int) ([]byte, error) 预读n字节不移动指针,用于协议头解析等场景。 注意:调用后无法使用UnreadByte/Rune直到下次读取。
UnreadByte() error 回退最后一个读取的字节,仅限前操作为读操作。
UnreadRune() error 回退最后一个读取的rune,仅限前操作为ReadRune。
3.1.7 跳过与清空
Discard(n int) (int, error) 跳过n字节,用于忽略无用数据段。
Reset(r io.Reader) 重置Reader,切换底层数据源并清空缓冲。 适用场景:复用Reader实例处理多个流。
3.1.8 高效数据转移
WriteTo(w io.Writer) (n int64, err error) 将数据直接写入w,利用底层WriteTo优化(如文件复制)。
当调用 bufio.Reader 的 WriteTo 方法时,其内部逻辑如下:
检查底层
io.Reader是否实现了io.WriterTo:如果 是,直接调用底层对象的
WriteTo方法,绕过bufio.Reader的缓冲区。如果 否,使用
bufio.Reader的缓冲机制,多次调用底层Read方法读取数据并写入w。
性能优化目的:
避免双重缓冲(底层和
bufio的缓冲同时存在),减少内存复制。利用底层可能存在的更高效写入实现(如零拷贝技术)。
3.1.9 状态查询
Buffered() int 返回缓冲区可读字节数,用于预判数据是否足够。
Size() int 获取缓冲区大小,用于调试或动态调整。
3.1.10 使用场景对比
方法
适用场景
注意事项
Read
通用数据读取
需处理部分读取和错误
ReadBytes/ReadString
按分隔符读取完整数据块
内存占用可能较高
ReadLine
需要处理超长行的低级操作
推荐使用 Scanner 替代
Peek
协议头检测或数据预判
避免后续Unread操作
Discard
跳过无用数据段
确保跳过的字节数有效
WriteTo
高效数据转移(如文件复制)
优先利用底层优化
3.2 Writer
bufio.Writer 为 io.Writer 提供缓冲功能,通过减少系统调用次数提升写入效率。
3.2.1 初始化
NewWriter(w io.Writer) *Writer 创建默认缓冲区大小(4096字节)的Writer。
NewWriterSize(w io.Writer, size int) *Writer 自定义缓冲区大小,适用于大文件写入或高频小数据场景。
3.2.2 写入数据
Write(p []byte) (int, error) 将数据写入缓冲区。若缓冲区空间不足,自动刷新后继续写入。 注意:返回的写入字节数可能小于len(p),需检查错误。
WriteString(s string) (int, error) 直接写入字符串,避免转换为[]byte。
WriteByte(c byte) error 写入单个字节,适用于二进制数据。
WriteRune(r rune) (int, error) 写入UTF-8字符,自动处理编码。
3.2.3 缓冲区管理
Flush() error 强制将缓冲区数据写入底层Writer,必须在写入结束后调用。
Available() int 返回缓冲区剩余空间,用于预判写入是否触发刷新。
AvailableBuffer() []byte(Go 1.18+) 返回一个可直接追加数据的空切片,需立即通过Write提交。
3.2.4 高效数据复制
ReadFrom(r io.Reader) (int64, error) 从Reader读取数据并写入缓冲区,优先调用底层ReadFrom实现优化性能。
3.2.5 状态查询与重置
Buffered() int 返回当前缓冲区中待刷新的字节数。
Reset(w io.Writer) 重置Writer,切换底层目标并清空缓冲区。
3.2.6 注意事项
错误处理:一旦写入出错,后续操作均返回相同错误,需及时终止并处理。
缓冲区刷新:程序退出或关键操作后必须调用
Flush,避免数据丢失。并发安全:
bufio.Writer非并发安全,需外部同步(如sync.Mutex)。性能权衡:
缓冲区大小:默认4KB适用于多数场景,大文件可调至64KB~1MB。
自动刷新:缓冲区满时自动触发,但需手动控制关键点刷新。
3.3 ReadWriter
ReadWriter 存储指向 Reader 和 Writer 的指针。它实现了 io.ReadWriter 。
3.4 Scanner
bufio.Scanner 是 Go 标准库中用于高效逐块读取数据的工具,尤其适合处理结构化文本(如按行分割的日志、单词或自定义格式)。
按标记(Token)分割输入流:支持按行、单词、字符或自定义规则分割。
自动缓冲管理:减少系统调用,提升读取效率。
轻量级 API:简化逐块读取逻辑,避免手动处理缓冲区和边界条件。
3.4.1 初始化与基础操作
NewScanner(r io.Reader) *Scanner 创建Scanner实例,默认分割函数为ScanLines(按行分割)。
Scan() bool 推进扫描器到下一个标记。返回true表示成功获取标记,false表示结束(EOF 或错误)。
3.4.2 获取标记内容
Text() string 返回当前标记的字符串(自动复制数据,安全但可能增加内存分配)。
Bytes() []byte 返回当前标记的字节切片(直接引用内部缓冲区,需立即使用或复制)。
3.4.3 分割函数与缓冲控制
Split(split SplitFunc) 设置自定义分割函数,支持以下内置函数:ScanLines:按行分割(默认)。ScanWords:按空格分割单词。ScanRunes:按 UTF-8 字符分割。ScanBytes:按字节分割。
Buffer(buf []byte, max int) 设置初始缓冲区和最大容量,避免因大标记导致错误。
3.4.4 错误处理
Err() error 返回扫描过程中首个非EOF错误。
3.4.5 底层逻辑与性能优化
3.1 缓冲机制
内部缓冲区:
Scanner维护一个动态缓冲区,按需从底层Reader读取数据。填充策略:当缓冲区无法容纳下一个标记时,自动扩容(受
Buffer方法约束)或触发错误。
3.2 分割函数(
SplitFunc)
函数签名:
data:当前缓冲区未处理的数据。atEOF:是否已读到输入流的末尾。advance:本次处理消耗的字节数。token:提取的标记(可为nil)。err:错误(如ErrFinalToken表示结束扫描)。自定义示例(按分号分割):
3.3 错误与恢复
不可恢复性:一旦遇到错误(如
ErrTooLong或 I/O 错误),扫描终止。缓冲区限制:默认最大标记大小为
bufio.MaxScanTokenSize(64KB),超出需显式设置Buffer。
最后更新于