bufio - 缓冲 I/O 操作
概述
bufio 包实现了带缓冲的 I/O 操作,通过在内存中维护缓冲区来减少系统调用次数,提高 I/O 性能。
包导入:
import "bufio"
基本使用:
示例 1:使用 Reader 读取一行
// 创建缓冲读取器(从标准输入读取)
reader := bufio.NewReader(os.Stdin)
// 读取一行(直到遇到换行符)
line, err := reader.ReadString('\n')
if err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Println("你输入的是:", line)
示例 2:使用 Writer 写入数据
// 创建缓冲写入器(写入到标准输出)
writer := bufio.NewWriter(os.Stdout)
// 写入数据到缓冲区
writer.WriteString("hello\n")
writer.WriteString("world\n")
// ⚠️ 重要:必须调用 Flush() 将缓冲区数据写入底层
// 忘记 Flush 会导致数据丢失!
err := writer.Flush()
if err != nil {
fmt.Println("刷新错误:", err)
return
}
示例 3:使用 Scanner 逐行读取文件
// 打开文件
file, err := os.Open("data.txt")
if err != nil {
fmt.Println("打开文件失败:", err)
return
}
defer file.Close() // 确保文件被关闭
// 创建 Scanner
scanner := bufio.NewScanner(file)
// Scan() 返回 true 表示成功读取一行
// 返回 false 表示读取结束或发生错误
for scanner.Scan() {
// Text() 返回当前行的内容(不包含换行符)
fmt.Println("读取:", scanner.Text())
}
// 检查是否有错误
if err := scanner.Err(); err != nil {
fmt.Println("读取文件错误:", err)
}
新手注意事项:
ReadString('\n')- 读取到换行符为止,返回的字符串包含换行符writer.Flush()- 必须调用,否则数据会丢失scanner.Scan()- 在循环中使用,返回 false 时结束scanner.Text()- 获取当前行内容,不包含换行符scanner.Err()- 循环结束后必须检查是否有错误
典型示例:
示例 1:读取文件并统计行数:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("data.txt")
if err != nil {
fmt.Println("打开文件失败:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
lines := 0
for scanner.Scan() {
lines++
}
if err := scanner.Err(); err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Printf("总行数:%d\n", lines)
}
运行:
$ go run main.go
总行数:100
示例 2:高性能文件复制:
package main
import (
"bufio"
"io"
"os"
)
func main() {
src, _ := os.Open("source.txt")
defer src.Close()
dst, _ := os.Create("dest.txt")
defer dst.Close()
// 使用缓冲提高性能
reader := bufio.NewReader(src)
writer := bufio.NewWriter(dst)
io.Copy(writer, reader)
writer.Flush() // 必须刷新缓冲区
}
示例 3:自定义 Scanner 分词规则(CSV 解析):
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
data := "apple,banana,cherry,date"
commaSplit := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
// 1. 先处理输入结束且无剩余数据的情况(最重要的修复!)
if atEOF && len(data) == 0 {
return 0, nil, nil // 正确终止
}
// 2. 查找逗号分隔符
for i := 0; i < len(data); i++ {
if data[i] == ',' {
return i + 1, data[:i], nil
}
}
/* 同上,不需要for循环,由index处理。可处理字节串
if i := bytes.Index(data, []byte(",")); i >= 0 {
return i + 1, data[:i], nil
}
*/
// 3. 如果已经到达流的末尾,返回剩下的所有数据(此时 data 非空)
if atEOF {
return len(data), data, nil
}
// 4. 还没到结尾,也没有找到逗号,需要更多数据
return 0, nil, nil
}
scanner := bufio.NewScanner(strings.NewReader(data))
scanner.Split(commaSplit)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("扫描错误:", err)
}
}
运行:
$ go run main.go
apple
banana
cherry
date
一、错误变量
缓冲区已满错误
ErrBufferFull
说明:
- 当缓冲区无法容纳更多数据时返回
- 常见于
ReadSlice、ReadLine方法 - 可通过增大缓冲区或使用
ReadBytes解决
定义:
var ErrBufferFull = errors.New("bufio: buffer full")
示例:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// 创建小缓冲区
r := bufio.NewReaderSize(os.Stdin, 4)
line, err := r.ReadSlice('\n')
if err == bufio.ErrBufferFull {
fmt.Println("缓冲区已满,请增大缓冲区或使用 ReadBytes")
}
_ = line
}
运行:
$ echo "hello world" | go run main.go
缓冲区已满,请增大缓冲区或使用 ReadBytes
Token 超长错误
ErrTooLong
说明:
- Scanner 读取的 token 超过最大限制时返回
- 默认限制为
MaxScanTokenSize(64KB) - 可通过
Scanner.Buffer()方法扩大限制
定义:
var ErrTooLong = errors.New("bufio: token too long")
示例:
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// 构造超长字符串(100KB)
data := strings.Repeat("a", 100000)
scanner := bufio.NewScanner(strings.NewReader(data))
for scanner.Scan() {
fmt.Println("读取:", len(scanner.Text()))
}
if err := scanner.Err(); err == bufio.ErrTooLong {
fmt.Println("错误:token 太长")
}
}
运行:
$ go run main.go
错误:token 太长
解决方案:
scanner := bufio.NewScanner(reader)
buf := make([]byte, 0, 64*1024)
scanner.Buffer(buf, 1024*1024) // 扩大到 1MB
Advance 超出范围错误
ErrAdvanceTooFar
说明:
- Scanner 的分词函数返回的 advance 值超出数据范围
- 通常由自定义
SplitFunc实现错误导致
定义:
var ErrAdvanceTooFar = errors.New("bufio: advance too far")
示例:
// 错误的 SplitFunc 实现
badSplit := func(data []byte, atEOF bool) (int, []byte, error) {
// advance 超出了数据长度
return len(data) + 10, data, nil
}
scanner.Split(badSplit)
// 会触发 ErrAdvanceTooFar 错误
读取计数异常错误
ErrBadReadCount
说明:
- 内部读取计数出现异常
- 较少见,通常表示底层 Reader 实现有问题
定义:
var ErrBadReadCount = errors.New("bufio: bad read count")
非法 UnreadByte 错误
ErrInvalidUnreadByte
说明:
- 在未读取任何字节时调用
UnreadByte() - 或连续调用多次
UnreadByte()
定义:
var ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
示例:
r := bufio.NewReader(strings.NewReader("hello"))
// 未读取就回退
err := r.UnreadByte()
if err == bufio.ErrInvalidUnreadByte {
fmt.Println("错误:未读取不能回退")
}
非法 UnreadRune 错误
ErrInvalidUnreadRune
说明:
- 在未读取任何 rune 时调用
UnreadRune() - 或连续调用多次
UnreadRune()
定义:
var ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
Advance 为负数错误
ErrNegativeAdvance
说明:
- Scanner 的分词函数返回负的 advance 值
- 由自定义
SplitFunc实现错误导致
定义:
var ErrNegativeAdvance = errors.New("bufio: negative advance")
读取计数为负数错误
ErrNegativeCount
说明:
- 内部读取返回负数的字节计数
- 表示底层 Reader 实现有严重错误
定义:
var ErrNegativeCount = errors.New("bufio: negative count")
Scanner 分词结束标记
ErrFinalToken
说明:
- 特殊的错误标记,用于自定义 SplitFunc
- 表示这是最后一个 token
- 不会导致 Scanner 报错
定义:
var ErrFinalToken = errors.New("bufio: final token")
示例:
// 自定义 SplitFunc 返回最后一个 token
split := func(data []byte, atEOF bool) (int, []byte, error) {
if atEOF && len(data) == 0 {
return 0, nil, bufio.ErrFinalToken
}
// ... 正常分词逻辑
return advance, token, nil
}
二、常量
Scanner 默认最大 Token 大小
MaxScanTokenSize
说明:
- Scanner 默认的 token 最大限制
- 值为 64 * 1024(64KB)
- 超过此限制会返回
ErrTooLong错误 - 可通过
Scanner.Buffer()方法调整
定义:
const MaxScanTokenSize = 64 * 1024
示例:
package main
import (
"bufio"
"fmt"
)
func main() {
fmt.Println("默认最大 token 大小:", bufio.MaxScanTokenSize)
// 输出:65536
}
扩大限制:
scanner := bufio.NewScanner(reader)
buf := make([]byte, 0, 64*1024)
scanner.Buffer(buf, 1024*1024) // 扩大到 1MB
三、构造函数
创建缓冲读取器
NewReader - 创建默认缓冲 Reader
说明:
- 创建带默认缓冲区大小的 Reader
- 默认缓冲区大小为 4096 字节(4KB)
- 返回
*bufio.Reader
定义:
func NewReader(rd io.Reader) *Reader
基本示例:
file, _ := os.Open("data.txt")
defer file.Close()
r := bufio.NewReader(file)
line, _ := r.ReadString('\n')
fmt.Println("读取:", line)
Reader 常用方法示例:
1. Read - 读取数据
说明: Read 方法用于从缓冲读取器中读取指定长度的数据到字节切片。它会优先从缓冲区返回数据,只有当缓冲区为空时才会从底层 io.Reader 读取新数据。这种方法适用于需要精确控制每次读取大小的场景,比如读取二进制文件(图片、视频等)、处理固定长度的数据记录、或者在内存受限的环境中控制内存使用。
典型使用场景:
- 读取大型二进制文件时精确控制内存使用
- 嵌入式设备或资源受限环境中节省内存
- 处理流媒体数据或网络数据流
- 实现自定义的读取逻辑和数据解析器
r := bufio.NewReader(strings.NewReader("hello world"))
buf := make([]byte, 5)
n, err := r.Read(buf)
if err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Printf("读取了 %d 字节:%s\n", n, string(buf))
// 输出:读取了 5 字节:hello
2. ReadString - 读取到分隔符
说明: ReadString 方法用于持续读取数据直到遇到指定的分隔符字节,返回包含该分隔符的字符串。这是读取文本文件最常用的方法,特别适合逐行读取(使用 ‘\n’ 作为分隔符)。它会持续读取数据,如果分隔符在当前缓冲区中不存在,会自动从底层读取更多数据。返回的字符串包含分隔符本身,这在处理文本行时非常有用。
典型使用场景:
- 逐行读取文本文件、日志文件和配置文件
- 读取 CSV 格式数据(使用 ‘,’ 作为分隔符)
- 解析自定义格式的文本文件(如 .ini、.conf 配置文件)
- 读取数据库导出的文本数据
r := bufio.NewReader(strings.NewReader("line1\nline2\nline3"))
line1, _ := r.ReadString('\n')
line2, _ := r.ReadString('\n')
fmt.Println(line1) // line1\n
fmt.Println(line2) // line2\n
3. ReadBytes - 读取到分隔符(返回 []byte)
使用场景:
- 需要处理二进制数据时
- 避免字符串转换的性能开销
- 读取网络协议数据
r := bufio.NewReader(strings.NewReader("hello\nworld"))
data, _ := r.ReadBytes('\n')
fmt.Printf("读取:%s", data) // hello\n
4. ReadByte - 读取单个字节
使用场景:
- 解析二进制协议
- 读取文件魔数(Magic Number)
- 逐字节处理数据
r := bufio.NewReader(strings.NewReader("ABC"))
b1, _ := r.ReadByte()
b2, _ := r.ReadByte()
b3, _ := r.ReadByte()
fmt.Printf("%c %c %c\n", b1, b2, b3)
// 输出:A B C
5. ReadRune - 读取单个 rune
使用场景:
- 处理包含中文等多字节字符的文本
- 统计字符数(而非字节数)
- 逐字符解析文本
r := bufio.NewReader(strings.NewReader("你好 Go"))
ch1, size1, _ := r.ReadRune()
ch2, size2, _ := r.ReadRune()
ch3, size3, _ := r.ReadRune()
fmt.Printf("%c (%d 字节)\n", ch1, size1) // 你 (3 字节)
fmt.Printf("%c (%d 字节)\n", ch2, size2) // 好 (3 字节)
fmt.Printf("%c (%d 字节)\n", ch3, size3) // G (1 字节)
6. Peek - 预读(不移动位置)
使用场景:
- 判断协议类型(如 HTTP、FTP)
- 检查文件头信息
- 预读数据以决定后续处理逻辑
r := bufio.NewReader(strings.NewReader("HTTP/1.1 200 OK"))
// 查看前 4 字节判断协议
header, _ := r.Peek(4)
if string(header) == "HTTP" {
fmt.Println("这是 HTTP 协议")
}
// 继续读取,仍然从开头开始
full, _ := r.ReadString(' ')
fmt.Println(full) // HTTP/1.1
7. Discard - 跳过字节
使用场景:
- 跳过文件头部信息
- 忽略不需要的数据
- 处理固定格式的文件
file, _ := os.Open("data.bin")
defer file.Close()
r := bufio.NewReader(file)
// 跳过 128 字节的文件头
discarded, _ := r.Discard(128)
fmt.Printf("跳过了 %d 字节\n", discarded)
// 读取实际数据
data, _ := io.ReadAll(r)
8. UnreadByte - 回退字节
使用场景:
- 解析时需要回退一个字节
- 读取分隔符后需要重新处理
- 实现简单的词法分析器
r := bufio.NewReader(strings.NewReader("12345"))
b1, _ := r.ReadByte()
fmt.Printf("%c\n", b1) // 1
// 回退
r.UnreadByte()
// 再次读取,仍然是 '1'
b2, _ := r.ReadByte()
fmt.Printf("%c\n", b2) // 1
9. UnreadRune - 回退 rune
使用场景:
- 解析多字节字符时回退
- 处理国际化文本
- 实现支持 Unicode 的词法分析器
r := bufio.NewReader(strings.NewReader("你好"))
rune1, size, _ := r.ReadRune()
fmt.Printf("%c (%d 字节)\n", rune1, size) // 你 (3 字节)
// 回退
r.UnreadRune()
// 再次读取,仍然是 '你'
rune2, _, _ := r.ReadRune()
fmt.Printf("%c\n", rune2) // 你
10. Buffered - 检查缓冲区状态
使用场景:
- 监控缓冲区使用情况
- 调试缓冲读取器性能
- 决定是否需要刷新
r := bufio.NewReader(strings.NewReader("hello world"))
// 先读取一些数据
r.ReadString(' ')
// 检查缓冲区还有多少数据
n := r.Buffered()
fmt.Println("缓冲区可读字节数:", n)
11. Reset - 复用 Reader
使用场景:
- 批量处理多个文件
- 避免重复创建 Reader(提高性能)
- 减少内存分配
// 创建一次
r := bufio.NewReader(nil)
// 多次复用
files := []string{"file1.txt", "file2.txt", "file3.txt"}
for _, filename := range files {
f, _ := os.Open(filename)
r.Reset(f)
// 使用 r 读取文件...
line, _ := r.ReadString('\n')
fmt.Println(line)
f.Close()
}
12. WriteTo - 写入到其他 io.Writer
使用场景:
- 快速复制文件
- 将数据写入网络连接
- 实现高效的文件传输
r := bufio.NewReader(strings.NewReader("hello world"))
// 直接写入到标准输出
n, _ := r.WriteTo(os.Stdout)
fmt.Printf("\n写入了 %d 字节\n", n)
// 输出:hello world
// 写入了 11 字节
NewReaderSize - 创建指定大小缓冲 Reader
说明:
- 创建指定缓冲区大小的 Reader
- size 小于 0 时使用默认大小(4096 字节)
- 适合需要精确控制缓冲区的场景
定义:
func NewReaderSize(rd io.Reader, size int) *Reader
基本示例:
file, _ := os.Open("data.txt")
defer file.Close()
// 创建 8KB 缓冲区
r := bufio.NewReaderSize(file, 8*1024)
line, _ := r.ReadString('\n')
fmt.Println("读取:", line)
Reader 常用方法示例:
1. Read - 读取数据
使用场景:
- 读取大文件时精确控制内存使用
- 嵌入式设备中节省内存
- 处理流媒体数据
r := bufio.NewReaderSize(file, 8*1024)
buf := make([]byte, 5)
n, err := r.Read(buf)
if err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Printf("读取了 %d 字节:%s\n", n, string(buf))
2. ReadString - 读取到分隔符
使用场景:
- 读取大型日志文件
- 处理数据库导出文件
- 逐行解析配置文件
r := bufio.NewReaderSize(file, 4*1024)
line1, _ := r.ReadString('\n')
line2, _ := r.ReadString('\n')
fmt.Println(line1)
fmt.Println(line2)
3. ReadBytes - 读取到分隔符(返回 []byte)
说明: ReadBytes 方法与 ReadString 功能类似,但返回字节切片而非字符串,避免了字符串转换的内存开销。当你需要直接处理二进制数据或追求高性能时,应该优先使用这个方法。它特别适合处理网络协议数据、二进制文件格式,或者需要将读取的数据直接传递给其他接受 []byte 的函数。
典型使用场景:
- 处理二进制协议数据(如 TCP/UDP 数据包)
- 读取网络数据流时避免不必要的字符串转换
- 需要将数据直接传递给接受 []byte 的函数
- 高性能场景中减少内存分配和拷贝
r := bufio.NewReaderSize(file, 2*1024)
data, _ := r.ReadBytes('\n')
fmt.Printf("读取:%s", data)
4. ReadByte - 读取单个字节
说明: ReadByte 方法用于读取并返回下一个字节,是最高效的单字节读取方式(无额外内存分配)。它常用于解析二进制文件格式、读取文件魔数(Magic Number)来判断文件类型,或者在实现自定义协议解析器时逐字节处理数据。相比调用 Read(buf) 读取单个字节,ReadByte 性能更好。
典型使用场景:
- 解析二进制文件格式(如 PNG、JPEG、PDF 等)
- 读取文件魔数(Magic Number)判断文件类型
- 实现自定义的网络协议解析器
- 词法分析器中逐字符处理输入
r := bufio.NewReaderSize(strings.NewReader("ABC"), 1024)
b1, _ := r.ReadByte()
b2, _ := r.ReadByte()
b3, _ := r.ReadByte()
fmt.Printf("%c %c %c\n", b1, b2, b3)
// 输出:A B C
5. ReadRune - 读取单个 rune
说明: ReadRune 方法用于读取并解码下一个 UTF-8 编码的 Unicode 字符(rune),返回字符本身、该字符占用的字节数以及可能的错误。这个方法自动处理 UTF-8 解码,非常适合处理包含中文、日文、表情符号等多字节字符的文本。当需要逐字符处理国际化文本、统计真实字符数(而非字节数)时,应该使用此方法。
典型使用场景:
- 处理包含中文、日文等多字节字符的国际化文本
- 统计真实的字符数(而非字节数)
- 实现支持 Unicode 的文本编辑器或词法分析器
- 逐字符解析和处理多语言文本
r := bufio.NewReaderSize(strings.NewReader("你好 Go"), 1024)
ch1, size1, _ := r.ReadRune()
ch2, size2, _ := r.ReadRune()
ch3, size3, _ := r.ReadRune()
fmt.Printf("%c (%d 字节)\n", ch1, size1) // 你 (3 字节)
fmt.Printf("%c (%d 字节)\n", ch2, size2) // 好 (3 字节)
fmt.Printf("%c (%d 字节)\n", ch3, size3) // G (1 字节)
6. Peek - 预读(不移动位置)
说明: Peek 方法用于查看接下来的 n 个字节数据,但不会移动读取位置,后续读取仍然从当前位置开始。这个方法非常适合需要先“偷看“数据内容以决定后续处理逻辑的场景,比如判断协议类型、检查文件头信息、识别数据格式等。返回的字节切片在下一次读取操作后会失效,不应长期保存。
典型使用场景:
- 判断网络协议类型(如 HTTP、FTP、SMTP)
- 检查文件头部信息识别文件类型
- 预读数据以决定使用何种解析逻辑
- 实现智能协议识别和自适应解析器
r := bufio.NewReaderSize(strings.NewReader("HTTP/1.1 200 OK"), 1024)
// 查看前 4 字节判断协议
header, _ := r.Peek(4)
if string(header) == "HTTP" {
fmt.Println("这是 HTTP 协议")
}
// 继续读取,仍然从开头开始
full, _ := r.ReadString(' ')
fmt.Println(full) // HTTP/1.1
7. Discard - 跳过字节
说明: Discard 方法用于跳过并丢弃指定数量的字节数据,返回实际跳过的字节数。这个方法非常适合处理包含固定格式头部或元数据的文件,可以跳过不需要的数据部分,直接读取有效内容。相比读取后丢弃,Discard 更高效,因为它只是移动读取位置而不需要实际复制数据。
典型使用场景:
- 跳过二进制文件的头部元数据(如图片、视频文件头)
- 忽略不需要的数据段或填充字节
- 处理固定格式的数据文件(如数据库文件、日志文件)
- 快速定位到文件中的特定位置
file, _ := os.Open("data.bin")
defer file.Close()
r := bufio.NewReaderSize(file, 4*1024)
// 跳过 128 字节的文件头
discarded, _ := r.Discard(128)
fmt.Printf("跳过了 %d 字节\n", discarded)
// 读取实际数据
data, _ := io.ReadAll(r)
8. UnreadByte - 回退字节
说明: UnreadByte 方法用于回退最近读取的一个字节,使得下次读取时会再次返回该字节。这个方法在实现词法分析器、协议解析器或任何需要“回看“逻辑的场景中非常有用。注意只能回退一个字节,连续调用会返回错误,且必须在成功读取字节后才能调用。
典型使用场景:
- 实现词法分析器时需要回退分隔符
- 协议解析中需要重新处理特定字节
- 解析器中的回退(backtrack)操作
- 读取到边界字符时需要重新处理
r := bufio.NewReaderSize(strings.NewReader("12345"), 1024)
b1, _ := r.ReadByte()
fmt.Printf("%c\n", b1) // 1
// 回退
r.UnreadByte()
// 再次读取,仍然是 '1'
b2, _ := r.ReadByte()
fmt.Printf("%c\n", b2) // 1
9. UnreadRune - 回退 rune
说明: UnreadRune 方法用于回退最近读取的一个 rune(Unicode 字符),使得下次读取时会再次返回该字符。与 UnreadByte 不同,它可以回退多字节字符(如中文),非常适合处理国际化文本的解析场景。同样只能回退一个 rune,连续调用会返回错误。
典型使用场景:
- Unicode 文本解析中的回退操作
- 多语言词法分析器实现
- 处理国际化文本时需要回看字符
- 字符边界检测和回退处理
r := bufio.NewReaderSize(strings.NewReader("你好"), 1024)
rune1, size, _ := r.ReadRune()
fmt.Printf("%c (%d 字节)\n", rune1, size) // 你 (3 字节)
// 回退
r.UnreadRune()
// 再次读取,仍然是 '你'
rune2, _, _ := r.ReadRune()
fmt.Printf("%c\n", rune2) // 你
10. Buffered - 检查缓冲区状态
说明: Buffered 方法用于返回缓冲区中当前可读的字节数,帮助开发者了解还有多少数据可以立即读取而无需访问底层 io.Reader。这个方法主要用于监控读取性能、调试缓冲区问题,或者在某些场景下决定是否需要等待更多数据。
典型使用场景:
- 监控和调试缓冲读取器的性能
- 检查是否还有数据可以立即读取
- 优化读取策略和缓冲区大小
- 实现基于缓冲区状态的自定义逻辑
r := bufio.NewReaderSize(strings.NewReader("hello world"), 4*1024)
// 先读取一些数据
r.ReadString(' ')
// 检查缓冲区还有多少数据
n := r.Buffered()
fmt.Println("缓冲区可读字节数:", n)
11. Reset - 复用 Reader
说明: Reset 方法用于将已有的 Reader 重置为读取新的 io.Reader,避免重新分配内存。这个方法在批量处理多个文件时非常有用,可以显著提高性能并减少内存分配开销。通过复用同一个 Reader 对象,可以避免重复创建和销毁带来的性能损失。
典型使用场景:
- 批量处理大量文件时提高性能
- 减少内存分配和垃圾回收压力
- 在循环中重复使用同一个 Reader 对象
- 实现高效的文件处理工具
// 创建一次(指定缓冲区大小)
r := bufio.NewReaderSize(nil, 8*1024)
// 多次复用
files := []string{"file1.txt", "file2.txt", "file3.txt"}
for _, filename := range files {
f, _ := os.Open(filename)
r.Reset(f)
// 使用 r 读取文件...
line, _ := r.ReadString('\n')
fmt.Println(line)
f.Close()
}
12. WriteTo - 写入到其他 io.Writer
说明: WriteTo 方法用于将 Reader 中的所有数据高效地写入到指定的 io.Writer 中,返回写入的字节数和错误。这个方法实现了 io.WriterTo 接口,可以进行零拷贝的高效数据传输,非常适合文件复制、数据转换输出和网络传输等场景。
典型使用场景:
- 高效实现文件复制功能
- 将读取的数据直接输出到标准输出
- 数据转换和格式转换输出
- 网络数据传输和转发
r := bufio.NewReaderSize(strings.NewReader("hello world"), 4*1024)
// 直接写入到标准输出
n, _ := r.WriteTo(os.Stdout)
fmt.Printf("\n写入了 %d 字节\n", n)
// 输出:hello world
// 写入了 11 字节
使用场景:
// 小缓冲区 - 节省内存
r1 := bufio.NewReaderSize(file, 1024) // 1KB
// 大缓冲区 - 提高性能
r2 := bufio.NewReaderSize(file, 64*1024) // 64KB
// 使用相同的方法(ReadString、ReadByte 等)
创建缓冲写入器
NewWriter - 创建默认缓冲 Writer
说明:
- 创建带默认缓冲区大小的 Writer
- 默认缓冲区大小为 4096 字节(4KB)
- 必须调用 Flush() 才能写入底层
定义:
func NewWriter(wr io.Writer) *Writer
基本示例:
file, _ := os.Create("output.txt")
defer file.Close()
w := bufio.NewWriter(file)
defer w.Flush() // 必须刷新
w.WriteString("hello world\n")
Writer 常用方法示例:
1. Write - 写入字节切片
说明: Write 方法用于将字节切片数据写入到缓冲区中。当缓冲区满时,会自动将数据刷新到底层 io.Writer。这是写入二进制数据的主要方法,适合批量写入字节数组或需要精确控制写入内容的场景。相比 WriteString,Write 更适合处理二进制数据或从其他来源获取的字节切片。
典型使用场景:
- 写入二进制数据(如图片、音频、视频数据)
- 批量写入字节数组提高性能
- 实现自定义的写入逻辑和数据序列化
- 将处理后的数据写入文件或网络
w := bufio.NewWriter(os.Stdout)
data := []byte("hello world")
n, err := w.Write(data)
if err != nil {
fmt.Println("写入错误:", err)
return
}
fmt.Printf("写入了 %d 字节\n", n)
w.Flush()
2. WriteString - 写入字符串
说明: WriteString 方法用于将字符串直接写入缓冲区,比使用 Write([]byte(s)) 更高效,因为它避免了字符串到字节切片的转换和内存分配。这是写入文本内容最常用的方法,适合写入文本文件、生成日志、导出数据等场景。
典型使用场景:
- 写入文本文件(最常用)
- 生成日志文件和报告
- 导出 CSV、JSON、XML 等格式的数据
- 批量写入大量文本内容
w := bufio.NewWriter(os.Stdout)
w.WriteString("hello\n")
w.WriteString("world\n")
w.Flush()
3. WriteByte - 写入单个字节
说明: WriteByte 方法用于写入单个字节到缓冲区,是最高效的写入方式(无内存分配)。它适合写入控制字符(如换行符、制表符)、构建二进制协议或逐字节构建数据格式。相比 Write([]byte{c}),WriteByte 性能更好且无需创建切片。
典型使用场景:
- 写入控制字符(换行符 \n、制表符 \t 等)
- 构建二进制协议数据格式
- 逐字节构建特定格式的数据
- 高性能场景中的单字节写入
w := bufio.NewWriter(os.Stdout)
// 逐字节写入
w.WriteByte('H')
w.WriteByte('e')
w.WriteByte('l')
w.WriteByte('l')
w.WriteByte('o')
w.WriteByte('\n')
w.Flush()
4. WriteRune - 写入单个 rune
说明: WriteRune 方法用于将单个 Unicode 字符(rune)写入缓冲区,自动进行 UTF-8 编码。它适合写入中文字符、日文、表情符号等多字节字符,是处理国际化文本的重要方法。在需要逐字符写入多语言文本时,应该使用此方法。
典型使用场景:
- 写入中文、日文等多字节字符
- 处理国际化文本和多语言内容
- 逐字符构建 Unicode 文本
- 实现支持多语言的文本编辑器
w := bufio.NewWriter(os.Stdout)
// 写入中文字符
w.WriteRune('你')
w.WriteRune('好')
w.WriteRune(',')
w.WriteRune('世')
w.WriteRune('界')
w.WriteRune('!')
w.WriteRune('\n')
w.Flush()
// 输出:你好,世界!
5. Flush - 刷新缓冲区
说明: Flush 方法用于将缓冲区中所有未写入的数据强制刷新到底层 io.Writer。这是 Writer 中最重要的方法,必须调用否则数据可能丢失。应该在 defer 中调用以确保数据被写入,或者在需要立即写入数据时手动调用。可以多次调用(幂等操作)。
典型使用场景:
- 程序退出前确保数据写入完成
- 实时日志系统中立即写入日志
- 事务性写入操作确保数据完整性
- 关键数据写入后立即刷新
file, _ := os.Create("output.txt")
defer file.Close()
w := bufio.NewWriter(file)
defer w.Flush() // 确保刷新
w.WriteString("重要数据\n")
// 程序退出前会自动 Flush
6. Buffered - 检查已缓冲字节数
使用场景:
- 监控缓冲区使用情况
- 调试写入性能
- 决定是否需要提前刷新
w := bufio.NewWriterSize(os.Stdout, 1024)
w.WriteString("hello")
w.WriteString("world")
n := w.Buffered()
fmt.Printf("已缓冲:%d 字节\n", n) // 已缓冲:10 字节
w.Flush()
fmt.Printf("已缓冲:%d 字节\n", w.Buffered()) // 已缓冲:0 字节
7. Available - 检查可用空间
使用场景:
- 检查是否还有空间写入
- 优化写入策略
- 避免缓冲区溢出
w := bufio.NewWriterSize(os.Stdout, 1024)
avail := w.Available()
fmt.Printf("可用空间:%d\n", avail) // 1024
w.WriteString("hello")
avail = w.Available()
fmt.Printf("可用空间:%d\n", avail) // 1019
8. Size - 获取缓冲区大小
使用场景:
- 验证缓冲区配置
- 调试性能问题
- 监控内存使用
w1 := bufio.NewWriter(os.Stdout)
fmt.Println("默认大小:", w1.Size()) // 4096
w2 := bufio.NewWriterSize(os.Stdout, 8192)
fmt.Println("自定义大小:", w2.Size()) // 8192
9. Reset - 复用 Writer
使用场景:
- 批量处理多个输出文件
- 避免重复创建 Writer(提高性能)
- 减少内存分配
// 创建一次
w := bufio.NewWriter(nil)
// 多次复用
files := []string{"out1.txt", "out2.txt", "out3.txt"}
for _, filename := range files {
f, _ := os.Create(filename)
w.Reset(f)
w.WriteString("内容\n")
w.Flush()
f.Close()
}
10. ReadFrom - 从其他 io.Reader 读取
使用场景:
- 快速复制文件
- 从网络接收数据并写入文件
- 实现高效的文件传输
// 从文件读取并写入到标准输出
file, _ := os.Open("input.txt")
defer file.Close()
w := bufio.NewWriter(os.Stdout)
defer w.Flush()
n, _ := w.ReadFrom(file)
fmt.Printf("复制了 %d 字节\n", n)
注意事项:
❌ 忘记 Flush:
w := bufio.NewWriter(file)
w.WriteString("data")
// 数据丢失!
✅ 正确的 defer 用法:
w := bufio.NewWriter(file)
defer w.Flush() // 确保刷新
❌ 频繁 Flush 影响性能:
for i := 0; i < 1000; i++ {
w.WriteString("line\n")
w.Flush() // 每次都刷新,性能差
}
✅ 批量刷新:
for i := 0; i < 1000; i++ {
w.WriteString("line\n")
}
w.Flush() // 最后一次性刷新
NewWriterSize - 创建指定大小缓冲 Writer
说明:
- 创建指定缓冲区大小的 Writer
- size 小于 0 时使用默认大小(4096 字节)
- 适合需要精确控制缓冲区的场景
定义:
func NewWriterSize(wr io.Writer, size int) *Writer
基本示例:
file, _ := os.Create("output.txt")
defer file.Close()
// 创建 16KB 缓冲区
w := bufio.NewWriterSize(file, 16*1024)
defer w.Flush()
w.WriteString("大数据量写入")
Writer 常用方法示例:
1. Write - 写入字节切片
使用场景:
- 精确控制缓冲区大小写入大数据
- 写入大型二进制文件
- 优化大数据写入性能
w := bufio.NewWriterSize(file, 8*1024)
data := []byte("hello world")
n, err := w.Write(data)
if err != nil {
fmt.Println("写入错误:", err)
return
}
fmt.Printf("写入了 %d 字节\n", n)
w.Flush()
2. WriteString - 写入字符串
使用场景:
- 生成大型日志文件
- 导出大量文本数据
- 批量写入文本内容
w := bufio.NewWriterSize(file, 4*1024)
w.WriteString("hello\n")
w.WriteString("world\n")
w.Flush()
3. WriteByte - 写入单个字节
使用场景:
- 构建二进制文件格式
- 写入控制字符
- 精确控制输出格式
w := bufio.NewWriterSize(os.Stdout, 1024)
// 逐字节写入
w.WriteByte('H')
w.WriteByte('e')
w.WriteByte('l')
w.WriteByte('l')
w.WriteByte('o')
w.WriteByte('\n')
w.Flush()
4. WriteRune - 写入单个 rune
使用场景:
- 写入多语言文本文件
- 生成国际化内容
- 处理 Unicode 字符
w := bufio.NewWriterSize(os.Stdout, 1024)
// 写入中文字符
w.WriteRune('你')
w.WriteRune('好')
w.WriteRune(',')
w.WriteRune('世')
w.WriteRune('界')
w.WriteRune('!')
w.WriteRune('\n')
w.Flush()
// 输出:你好,世界!
5. Flush - 刷新缓冲区
使用场景:
- 确保关键数据立即写入
- 实时日志系统
- 事务性写入操作
w := bufio.NewWriterSize(file, 4*1024)
defer w.Flush() // 确保刷新
w.WriteString("重要数据\n")
// 程序退出前会自动 Flush
6. Buffered - 检查已缓冲字节数
说明: Buffered 方法用于返回当前已写入但尚未刷新到底层的字节数。这个方法主要用于监控写入进度、调试性能问题,或者决定是否需要提前刷新缓冲区。通过检查已缓冲的字节数,可以优化写入策略和排查数据未正确写入的问题。
典型使用场景:
- 监控写入进度和缓冲区使用情况
- 调试性能问题和数据写入异常
- 优化刷新策略(何时调用 Flush)
- 实现基于缓冲区状态的自定义逻辑
w := bufio.NewWriterSize(os.Stdout, 1024)
w.WriteString("hello")
w.WriteString("world")
n := w.Buffered()
fmt.Printf("已缓冲:%d 字节\n", n) // 已缓冲:10 字节
w.Flush()
fmt.Printf("已缓冲:%d 字节\n", w.Buffered()) // 已缓冲:0 字节
7. Available - 检查可用空间
说明: Available 方法用于返回缓冲区中剩余的可用空间。它适合在写入前检查是否还有足够空间,防止缓冲区溢出。在内存敏感的应用或需要动态调整写入策略的场景中非常有用。
典型使用场景:
- 写入前检查缓冲区是否有足够空间
- 防止缓冲区溢出和数据丢失
- 动态调整写入策略和批次大小
- 内存敏感应用中的资源管理
w := bufio.NewWriterSize(os.Stdout, 1024)
avail := w.Available()
fmt.Printf("可用空间:%d\n", avail) // 1024
w.WriteString("hello")
avail = w.Available()
fmt.Printf("可用空间:%d\n", avail) // 1019
8. Size - 获取缓冲区大小
说明: Size 方法用于返回 Writer 的缓冲区总大小。这个方法主要用于验证配置是否正确、进行性能调优和监控资源使用情况。在调试性能问题时,可以通过检查缓冲区大小来确认是否使用了预期的配置。
典型使用场景:
- 验证缓冲区配置是否符合预期
- 性能调优时检查缓冲区设置
- 监控和管理内存使用
- 调试写入性能问题
w := bufio.NewWriterSize(os.Stdout, 8192)
fmt.Println("缓冲区大小:", w.Size()) // 8192
9. Reset - 复用 Writer
说明: Reset 方法用于将已有的 Writer 重置为写入新的 io.Writer,避免重新分配内存。这个方法在批量处理多个输出文件时非常有用,可以显著提高性能并减少内存分配开销。通过复用同一个 Writer 对象,可以避免重复创建和销毁带来的性能损失。
典型使用场景:
- 批量生成多个文件时提高性能
- 减少内存分配和垃圾回收压力
- 在循环中重复使用同一个 Writer 对象
- 实现高效的文件生成工具
// 创建一次(指定缓冲区大小)
w := bufio.NewWriterSize(nil, 8*1024)
// 多次复用
files := []string{"out1.txt", "out2.txt", "out3.txt"}
for _, filename := range files {
f, _ := os.Create(filename)
w.Reset(f)
w.WriteString("内容\n")
w.Flush()
f.Close()
}
10. ReadFrom - 从其他 io.Reader 读取
说明: ReadFrom 方法用于从指定的 io.Reader 中读取所有数据并写入到缓冲区中,实现了 io.ReaderFrom 接口。这个方法适合高效地复制文件、从网络接收数据并写入文件,或者实现数据转换和转发功能。
典型使用场景:
- 高效实现文件复制功能(指定缓冲区大小)
- 从网络接收数据并写入本地文件
- 数据转换、过滤和格式化
- 网络数据转发和代理
// 从文件读取并写入到标准输出
file, _ := os.Open("input.txt")
defer file.Close()
w := bufio.NewWriterSize(os.Stdout, 4*1024)
defer w.Flush()
n, _ := w.ReadFrom(file)
fmt.Printf("复制了 %d 字节\n", n)
使用场景:
// 小缓冲区 - 节省内存
w1 := bufio.NewWriterSize(file, 1024) // 1KB
// 大缓冲区 - 提高性能
w2 := bufio.NewWriterSize(file, 64*1024) // 64KB
// 使用相同的方法(WriteString、Flush 等)
创建 Scanner
NewScanner - 创建 Scanner
说明:
- 创建 Scanner 用于逐 token 读取
- 默认使用
ScanLines分词(按行读取) - 可通过
Split()方法修改分词规则
定义:
func NewScanner(r io.Reader) *Scanner
基本示例:
file, _ := os.Open("data.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("错误:", err)
}
Scanner 常用方法示例:
1. Scan - 读取下一个 token
说明: Scan 方法用于读取下一个 token(默认为一行文本),返回 true 表示成功读取,返回 false 表示读取结束或发生错误。这是 Scanner 最核心的方法,必须在 for 循环中使用,每次调用都会自动读取并分词。当遇到文件末尾、读取错误或 token 超长时,返回 false。
典型使用场景:
- 逐行读取文本文件(最常见用法)
- 遍历和处理文本数据流
- 实现简单的文本解析器
- 读取和处理日志文件
scanner := bufio.NewScanner(strings.NewReader("line1\nline2\nline3"))
// 标准用法
for scanner.Scan() {
// 成功读取一行
fmt.Println(scanner.Text())
}
// Scan() 返回 false,循环结束
2. Text - 获取当前 token 字符串
说明: Text 方法用于返回当前 token 的字符串表示,必须在 Scan() 返回 true 后调用。返回的字符串在下一次 Scan() 调用后会失效(底层数据被覆盖),所以如果需要长期保存,必须复制到切片或其他数据结构中。
典型使用场景:
- 获取并处理读取到的文本内容
- 保存读取的行到数组中长期使用
- 对文本内容进行字符串操作和分析
- 提取和处理文本数据
scanner := bufio.NewScanner(strings.NewReader("hello\nworld"))
if scanner.Scan() {
text := scanner.Text()
fmt.Println("第一行:", text) // hello
}
if scanner.Scan() {
text := scanner.Text()
fmt.Println("第二行:", text) // world
}
3. Bytes - 获取当前 token 字节切片
说明: Bytes 方法与 Text 方法类似,但返回当前 token 的字节切片而非字符串,避免了字符串转换的内存开销。当需要直接处理二进制数据或追求高性能时,应该使用此方法。返回的切片在下一次 Scan() 调用后同样会失效。
典型使用场景:
- 需要直接处理字节数据而非字符串
- 避免字符串转换的性能开销
- 将数据传递给接受 []byte 的函数
- 高性能场景中的数据处理
scanner := bufio.NewScanner(strings.NewReader("hello\nworld"))
for scanner.Scan() {
data := scanner.Bytes()
// 直接处理字节切片,避免转换
fmt.Printf("读取:%s\n", data)
}
4. Err - 检查错误
说明: Err 方法用于返回读取过程中发生的错误,必须在 Scan() 循环结束后调用检查。这是 Scanner 使用中非常重要的一个方法,可以区分正常读取结束(返回 nil)和异常错误(返回错误对象)。常见的错误包括 ErrTooLong(token 超长)和底层读取错误。
典型使用场景:
- 读取结束后检查是否有错误发生
- 区分正常结束和异常错误
- 实现健壮的文件读取逻辑
- 错误处理和日志记录
scanner := bufio.NewScanner(file)
for scanner.Scan() {
process(scanner.Text())
}
// 必须检查错误
if err := scanner.Err(); err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Println("读取完成")
5. Split - 设置分词函数
说明: Split 方法用于设置自定义的分词函数,改变 Scanner 默认的按行分割行为。通过提供自定义的 SplitFunc,可以实现按单词分割、按字符分割、或者按照特定格式(如 CSV、JSON)解析数据。这个方法必须在第一次调用 Scan() 之前设置,否则会影响已经读取的数据。bufio 包提供了四个内置的分词函数:ScanLines(按行)、ScanWords(按单词)、ScanBytes(按字节)、ScanRunes(按字符)。
典型使用场景:
- 按单词分割文本进行词频统计(使用 ScanWords)
- 自定义分词规则解析 CSV 或 JSON 数据
- 实现特定格式的文本解析器
- 处理特殊数据格式(如固定宽度字段)
// 按单词分割
scanner := bufio.NewScanner(strings.NewReader("go is fun"))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 输出:go, is, fun
6. Buffer - 设置缓冲区大小
说明: Buffer 方法用于设置 Scanner 的缓冲区和最大 token 大小。默认最大 token 为 64KB(MaxScanTokenSize),当读取的单行数据超过此限制时会返回 ErrTooLong 错误。通过此方法可以扩大限制,处理包含超长行的文件(如大型日志文件、数据库导出文件)。第一个参数是初始缓冲区切片,第二个参数是最大 token 大小。这个方法必须在第一次调用 Scan() 之前设置。
典型使用场景:
- 读取包含超长行的日志文件(超过 64KB)
- 处理大型数据库导出文件
- 避免 Token 太长错误导致读取失败
- 处理特殊格式的长数据行(如 Base64 编码数据)
scanner := bufio.NewScanner(file)
// 扩大缓冲区到 1MB
buf := make([]byte, 0, 64*1024)
scanner.Buffer(buf, 1024*1024)
for scanner.Scan() {
line := scanner.Text()
// 处理超长行...
}
使用场景:
1. 逐行读取文件
file, _ := os.Open("data.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
lineNum := 0
for scanner.Scan() {
lineNum++
fmt.Printf("第%d行:%s\n", lineNum, scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("读取失败:", err)
}
2. 按单词分割
scanner := bufio.NewScanner(strings.NewReader("go is awesome"))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println("单词:", scanner.Text())
}
3. 统计行数
scanner := bufio.NewScanner(file)
lines := 0
for scanner.Scan() {
lines++
}
if err := scanner.Err(); err != nil {
fmt.Println("读取失败:", err)
return
}
fmt.Println("总行数:", lines)
4. 保存所有行
scanner := bufio.NewScanner(file)
var allLines []string
for scanner.Scan() {
// Text() 返回的字符串已复制,可以安全保存
allLines = append(allLines, scanner.Text())
}
注意事项:
❌ 忘记检查错误:
for scanner.Scan() {
process(scanner.Text())
}
// 忘记检查 Err()!
✅ 正确做法:
for scanner.Scan() {
process(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("错误:", err)
}
⚠️ Token 长度限制:
- 默认最大 64KB(
MaxScanTokenSize) - 超过会返回
ErrTooLong错误 - 使用
Buffer()方法扩大限制
NewReadWriter - 创建读写组合
说明:
- 将 Reader 和 Writer 组合成 ReadWriter
- 同时提供读取和写入功能
- 适合需要双向通信的场景(如网络连接)
定义:
func NewReadWriter(r *Reader, w *Writer) *ReadWriter
示例:
r := bufio.NewReader(os.Stdin)
w := bufio.NewWriter(os.Stdout)
rw := bufio.NewReadWriter(r, w)
fmt.Print("输入内容:")
line, _ := rw.ReadString('\n')
rw.WriteString("你输入的是:" + line)
rw.Flush()
运行:
$ go run main.go
输入内容:hello
你输入的是:hello
使用场景:
// 网络连接中的双向通信
conn, _ := net.Dial("tcp", "localhost:8080")
rw := bufio.NewReadWriter(
bufio.NewReader(conn),
bufio.NewWriter(conn),
)
// 发送请求
rw.WriteString("GET / HTTP/1.1\r\n")
rw.Flush()
// 读取响应
response, _ := rw.ReadString('\n')
fmt.Println(response)
四、核心类型
缓冲读取器
Reader
定义:
type Reader struct {
// 内部字段,不应直接访问
}
说明:
- 实现了
io.Reader、io.WriterTo、io.ByteReader、io.ByteScanner、io.RuneReader、io.RuneScanner接口 - 内部维护缓冲区,减少系统调用
- 适合读取大量小数据块的场景
主要方法详解:
Read 方法
定义:
func (b *Reader) Read(p []byte) (n int, err error)
说明:
- 从底层读取器读取数据到缓冲区,再返回给调用者
- 优先从缓冲区返回数据,缓冲区空时才从底层读取
- 返回读取的字节数和可能的错误
示例:
r := bufio.NewReader(strings.NewReader("hello world"))
buf := make([]byte, 5)
n, err := r.Read(buf)
if err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Printf("读取了 %d 字节:%s\n", n, string(buf))
// 输出:读取了 5 字节:hello
ReadString 方法
定义:
func (b *Reader) ReadString(delim byte) (string, error)
说明:
- 读取数据直到第一次出现 delim 字节
- 返回的字符串包含 delim
- 常用于读取一行(delim = ‘\n’)
示例:
r := bufio.NewReader(strings.NewReader("line1\nline2\nline3"))
line1, _ := r.ReadString('\n')
line2, _ := r.ReadString('\n')
fmt.Println(line1) // line1\n
fmt.Println(line2) // line2\n
ReadBytes 方法
定义:
func (b *Reader) ReadBytes(delim byte) ([]byte, error)
说明:
- 类似 ReadString,但返回
[]byte - 避免不必要的字符串转换时使用
- 返回的字节切片包含 delim
示例:
r := bufio.NewReader(strings.NewReader("hello\nworld"))
data, _ := r.ReadBytes('\n')
fmt.Printf("读取:%s", data) // hello\n
// 转换为字符串(如果需要)
str := string(data)
ReadByte 方法
定义:
func (b *Reader) ReadByte() (byte, error)
说明:
- 读取并返回下一个字节
- 比 Read 更高效(针对单字节)
- 返回单个字节和可能的错误
示例:
r := bufio.NewReader(strings.NewReader("ABC"))
b1, _ := r.ReadByte()
b2, _ := r.ReadByte()
b3, _ := r.ReadByte()
fmt.Printf("%c %c %c\n", b1, b2, b3)
// 输出:A B C
ReadRune 方法
定义:
func (b *Reader) ReadRune() (r rune, size int, err error)
说明:
- 读取并返回下一个 rune(Unicode 字符)
- 自动处理 UTF-8 解码
- 返回 rune、占用的字节数、错误
示例:
r := bufio.NewReader(strings.NewReader("你好 Go"))
ch1, size1, _ := r.ReadRune()
ch2, size2, _ := r.ReadRune()
ch3, size3, _ := r.ReadRune()
fmt.Printf("%c (%d 字节)\n", ch1, size1) // 你 (3 字节)
fmt.Printf("%c (%d 字节)\n", ch2, size2) // 好 (3 字节)
fmt.Printf("%c (%d 字节)\n", ch3, size3) // G (1 字节)
Peek 方法
定义:
func (b *Reader) Peek(n int) ([]byte, error)
说明:
- 查看接下来的 n 个字节(不移动读取位置)
- 返回的字节切片在下一次读取时会失效
- 常用于预读协议头、魔数等
示例:
r := bufio.NewReader(strings.NewReader("HTTP/1.1 200 OK"))
// 查看前 4 字节判断协议
header, _ := r.Peek(4)
if string(header) == "HTTP" {
fmt.Println("这是 HTTP 协议")
}
// 继续读取,仍然从开头开始
full, _ := r.ReadString(' ')
fmt.Println(full) // HTTP/1.1
Discard 方法
定义:
func (b *Reader) Discard(n int) (int, error)
说明:
- 跳过并丢弃 n 个字节
- 用于跳过不需要的数据(如文件头)
- 返回实际丢弃的字节数
示例:
// 假设文件前 128 字节是文件头
file, _ := os.Open("data.bin")
defer file.Close()
r := bufio.NewReader(file)
// 跳过文件头
discarded, _ := r.Discard(128)
fmt.Printf("跳过了 %d 字节\n", discarded)
// 读取实际数据
data, _ := io.ReadAll(r)
UnreadByte 方法
定义:
func (b *Reader) UnreadByte() error
说明:
- 回退一个字节(下次读取会再次返回该字节)
- 只能回退最近读取的一个字节
- 连续调用会返回错误
示例:
r := bufio.NewReader(strings.NewReader("12345"))
b1, _ := r.ReadByte()
fmt.Printf("%c\n", b1) // 1
// 回退
err := r.UnreadByte()
if err != nil {
fmt.Println("回退失败:", err)
}
// 再次读取,仍然是 '1'
b2, _ := r.ReadByte()
fmt.Printf("%c\n", b2) // 1
UnreadRune 方法
定义:
func (b *Reader) UnreadRune() error
说明:
- 回退一个 rune(下次读取会再次返回该 rune)
- 只能回退最近读取的一个 rune
- 连续调用会返回错误
示例:
r := bufio.NewReader(strings.NewReader("你好"))
rune1, size, _ := r.ReadRune()
fmt.Printf("%c (%d 字节)\n", rune1, size) // 你 (3 字节)
// 回退
r.UnreadRune()
// 再次读取,仍然是 '你'
rune2, _, _ := r.ReadRune()
fmt.Printf("%c\n", rune2) // 你
Buffered 方法
定义:
func (b *Reader) Buffered() int
说明:
- 返回缓冲区中可读的字节数
- 用于检查还有多少数据可以立即读取
示例:
r := bufio.NewReader(strings.NewReader("hello world"))
// 先读取一些数据
r.ReadString(' ')
// 检查缓冲区还有多少数据
n := r.Buffered()
fmt.Println("缓冲区可读字节数:", n)
Reset 方法
定义:
func (b *Reader) Reset(rd io.Reader)
说明:
- 重置为读取新的 io.Reader
- 复用 Reader,避免重新分配
- 适合需要重复使用 Reader 的场景
示例:
// 创建一次
r := bufio.NewReader(nil)
// 多次复用
files := []string{"file1.txt", "file2.txt", "file3.txt"}
for _, filename := range files {
f, _ := os.Open(filename)
r.Reset(f)
// 使用 r 读取文件...
line, _ := r.ReadString('\n')
fmt.Println(line)
f.Close()
}
WriteTo 方法
定义:
func (b *Reader) WriteTo(w io.Writer) (n int64, err error)
说明:
- 将 Reader 中的所有数据写入到 w
- 返回写入的字节数和错误
- 实现了
io.WriterTo接口
示例:
r := bufio.NewReader(strings.NewReader("hello world"))
// 直接写入到标准输出
n, _ := r.WriteTo(os.Stdout)
fmt.Printf("\n写入了 %d 字节\n", n)
// 输出:hello world
// 写入了 11 字节
综合示例:
r := bufio.NewReader(strings.NewReader("hello\nworld\n"))
line1, _ := r.ReadString('\n')
line2, _ := r.ReadString('\n')
fmt.Println(line1) // hello\n
fmt.Println(line2) // world\n
示例 2:Peek 预读:
r := bufio.NewReader(strings.NewReader("HTTP/1.1 200 OK"))
// 查看前 4 字节判断协议
header, _ := r.Peek(4)
if string(header) == "HTTP" {
fmt.Println("HTTP 协议")
}
// 继续读取,仍然从开头开始
full, _ := r.ReadString(' ')
fmt.Println(full) // HTTP/1.1
示例 3:跳过文件头:
file, _ := os.Open("data.bin")
defer file.Close()
r := bufio.NewReader(file)
// 跳过 128 字节的文件头
r.Discard(128)
// 读取实际数据
data, _ := io.ReadAll(r)
示例 4:字节回退:
r := bufio.NewReader(strings.NewReader("12345"))
b1, _ := r.ReadByte()
fmt.Printf("%c\n", b1) // 1
// 回退
r.UnreadByte()
// 再次读取,仍然是 '1'
b2, _ := r.ReadByte()
fmt.Printf("%c\n", b2) // 1
示例 5:复用 Reader:
// 创建一次
r := bufio.NewReader(nil)
// 多次复用
for _, file := range files {
f, _ := os.Open(file)
r.Reset(f)
// 使用 r 读取...
f.Close()
}
缓冲写入器
Writer
定义:
type Writer struct {
// 内部字段,不应直接访问
}
说明:
- 实现了
io.Writer、io.ByteWriter、io.StringWriter、io.RuneWriter、io.WriterTo接口 - 内部维护缓冲区,减少系统调用
- 必须调用 Flush() 才能将数据写入底层
主要方法详解:
Write 方法
定义:
func (b *Writer) Write(p []byte) (n int, err error)
说明:
- 写入字节切片到缓冲区
- 缓冲区满时会自动 Flush 到底层
- 返回写入的字节数和错误
示例:
w := bufio.NewWriter(os.Stdout)
data := []byte("hello world")
n, err := w.Write(data)
if err != nil {
fmt.Println("写入错误:", err)
return
}
fmt.Printf("写入了 %d 字节\n", n)
w.Flush()
WriteString 方法
定义:
func (b *Writer) WriteString(s string) (n int, err error)
说明:
- 写入字符串到缓冲区
- 比
Write([]byte(s))更高效(避免内存分配) - 返回写入的字节数和错误
示例:
w := bufio.NewWriter(os.Stdout)
w.WriteString("hello\n")
w.WriteString("world\n")
w.Flush()
WriteByte 方法
定义:
func (b *Writer) WriteByte(c byte) error
说明:
- 写入单个字节到缓冲区
- 最高效的写入方式(无内存分配)
- 只返回错误
示例:
w := bufio.NewWriter(os.Stdout)
// 逐字节写入
w.WriteByte('H')
w.WriteByte('e')
w.WriteByte('l')
w.WriteByte('l')
w.WriteByte('o')
w.WriteByte('\n')
w.Flush()
WriteRune 方法
定义:
func (b *Writer) WriteRune(r rune) (n int, err error)
说明:
- 写入单个 rune(Unicode 字符)到缓冲区
- 自动进行 UTF-8 编码
- 返回写入的字节数和错误
示例:
w := bufio.NewWriter(os.Stdout)
// 写入中文字符
w.WriteRune('你')
w.WriteRune('好')
w.WriteRune(',')
w.WriteRune('世')
w.WriteRune('界')
w.WriteRune('!')
w.WriteRune('\n')
w.Flush()
// 输出:你好,世界!
Flush 方法
定义:
func (b *Writer) Flush() error
说明:
- 将缓冲区所有数据写入底层 io.Writer
- 必须调用,否则数据可能丢失
- 应该在 defer 中调用确保刷新
- 可以多次调用(幂等)
示例:
file, _ := os.Create("output.txt")
defer file.Close()
w := bufio.NewWriter(file)
defer w.Flush() // 确保刷新
w.WriteString("重要数据\n")
// 程序退出前会自动 Flush
Buffered 方法
定义:
func (b *Writer) Buffered() int
说明:
- 返回缓冲区中已写入但未刷新的字节数
- 用于检查还有多少数据等待刷新
示例:
w := bufio.NewWriterSize(os.Stdout, 1024)
w.WriteString("hello")
w.WriteString("world")
n := w.Buffered()
fmt.Printf("已缓冲:%d 字节\n", n) // 已缓冲:10 字节
w.Flush()
fmt.Printf("已缓冲:%d 字节\n", w.Buffered()) // 已缓冲:0 字节
Available 方法
定义:
func (b *Writer) Available() int
说明:
- 返回缓冲区可用空间
- 等于
Size() - Buffered()
示例:
w := bufio.NewWriterSize(os.Stdout, 1024)
avail := w.Available()
fmt.Printf("可用空间:%d\n", avail) // 1024
w.WriteString("hello")
avail = w.Available()
fmt.Printf("可用空间:%d\n", avail) // 1019
AvailableBuffer 方法
定义:
func (b *Writer) AvailableBuffer() []byte
说明:
- 返回可用的缓冲区切片
- 直接写入该切片不会更新 Writer 状态
- 用于需要直接操作缓冲区的场景
示例:
w := bufio.NewWriterSize(os.Stdout, 1024)
// 获取可用缓冲区
buf := w.AvailableBuffer()
fmt.Printf("缓冲区大小:%d\n", len(buf))
// 注意:直接写入 buf 不会更新 w 的状态
// 应该使用 w.Write() 或 w.WriteString()
Size 方法
定义:
func (b *Writer) Size() int
说明:
- 返回缓冲区大小
- 创建时确定,不可更改
示例:
w1 := bufio.NewWriter(os.Stdout)
fmt.Println("默认大小:", w1.Size()) // 4096
w2 := bufio.NewWriterSize(os.Stdout, 8192)
fmt.Println("自定义大小:", w2.Size()) // 8192
Reset 方法
定义:
func (b *Writer) Reset(wr io.Writer)
说明:
- 重置为写入新的 io.Writer
- 复用 Writer,避免重新分配
- 适合需要重复使用 Writer 的场景
示例:
// 创建一次
w := bufio.NewWriter(nil)
// 多次复用
files := []string{"out1.txt", "out2.txt", "out3.txt"}
for _, filename := range files {
f, _ := os.Create(filename)
w.Reset(f)
w.WriteString("内容\n")
w.Flush()
f.Close()
}
ReadFrom 方法
定义:
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error)
说明:
- 从 r 读取所有数据并写入缓冲区
- 实现了
io.ReaderFrom接口 - 返回读取的字节数和错误
示例:
// 从文件读取并写入到标准输出
file, _ := os.Open("input.txt")
defer file.Close()
w := bufio.NewWriter(os.Stdout)
defer w.Flush()
n, _ := w.ReadFrom(file)
fmt.Printf("复制了 %d 字节\n", n)
综合示例:
file, _ := os.Create("output.txt")
defer file.Close()
w := bufio.NewWriter(file)
defer w.Flush()
w.WriteString("hello world\n")
w.WriteByte('A')
w.WriteRune('中')
示例 2:高性能批量写入:
file, _ := os.Create("data.txt")
defer file.Close()
w := bufio.NewWriter(file)
defer w.Flush()
// 1000 次小写入合并为几次系统调用
for i := 0; i < 1000; i++ {
w.WriteString(fmt.Sprintf("line %d\n", i))
}
// 最后一次性刷新
示例 3:网络传输优化:
conn, _ := net.Dial("tcp", "localhost:8080")
defer conn.Close()
w := bufio.NewWriter(conn)
defer w.Flush()
// 多次小写入合并为一次网络发送
w.WriteString("GET / HTTP/1.1\r\n")
w.WriteString("Host: localhost\r\n")
w.WriteString("\r\n")
示例 4:构建大字符串:
var buf strings.Builder
w := bufio.NewWriter(&buf)
for i := 0; i < 10000; i++ {
w.WriteString(fmt.Sprintf("%d,", i))
}
w.Flush()
result := buf.String()
示例 5:检查缓冲区状态:
w := bufio.NewWriterSize(os.Stdout, 4096)
w.WriteString("hello")
fmt.Println("缓冲区大小:", w.Size()) // 4096
fmt.Println("已缓冲:", w.Buffered()) // 5
fmt.Println("可用空间:", w.Available()) // 4091
示例 6:复用 Writer:
// 创建一次
w := bufio.NewWriter(nil)
// 多次复用
for _, file := range outputFiles {
f, _ := os.Create(file)
w.Reset(f)
// 使用 w 写入...
w.Flush()
f.Close()
}
注意事项:
❌ 忘记 Flush:
w := bufio.NewWriter(file)
w.WriteString("data")
// 数据丢失!
✅ 正确的 defer 用法:
w := bufio.NewWriter(file)
defer w.Flush() // 确保刷新
❌ 频繁 Flush 影响性能:
for i := 0; i < 1000; i++ {
w.WriteString("line\n")
w.Flush() // 每次都刷新,性能差
}
✅ 批量刷新:
for i := 0; i < 1000; i++ {
w.WriteString("line\n")
}
w.Flush() // 最后一次性刷新
扫描器
Scanner
定义:
type Scanner struct {
// 内部字段,不应直接访问
}
说明:
- 用于逐 token 读取文本
- 内部维护缓冲区,自动处理分词
- 适合简单的文本解析场景
- 不适合复杂解析(应使用
encoding/json等专用包)
主要方法详解:
Scan 方法
定义:
func (s *Scanner) Scan() bool
说明:
- 读取下一个 token
- 返回 true 表示成功读取
- 返回 false 表示结束或错误
- 必须在循环中使用
示例:
scanner := bufio.NewScanner(strings.NewReader("line1\nline2\nline3"))
// 标准用法
for scanner.Scan() {
// 成功读取一行
fmt.Println(scanner.Text())
}
// Scan() 返回 false,循环结束
Text 方法
定义:
func (s *Scanner) Text() string
说明:
- 返回当前 token 的字符串
- 必须在 Scan() 返回 true 后调用
- 返回的字符串在下一次 Scan() 后会失效
- 需要长期保存时应复制到切片
示例:
scanner := bufio.NewScanner(strings.NewReader("hello\nworld"))
if scanner.Scan() {
text := scanner.Text()
fmt.Println("第一行:", text) // hello
}
if scanner.Scan() {
text := scanner.Text()
fmt.Println("第二行:", text) // world
}
Bytes 方法
定义:
func (s *Scanner) Bytes() []byte
说明:
- 返回当前 token 的字节切片
- 类似 Text(),但返回
[]byte - 避免字符串转换时使用
- 返回的切片在下一次 Scan() 后会失效
示例:
scanner := bufio.NewScanner(strings.NewReader("hello\nworld"))
for scanner.Scan() {
data := scanner.Bytes()
// 直接处理字节切片,避免转换
fmt.Printf("读取:%s\n", data)
}
Err 方法
定义:
func (s *Scanner) Err() error
说明:
- 返回读取过程中的错误
- 循环结束后必须检查
- 可以区分正常结束和错误
- 如果返回 nil 表示正常结束
示例:
scanner := bufio.NewScanner(file)
for scanner.Scan() {
process(scanner.Text())
}
// 必须检查错误
if err := scanner.Err(); err != nil {
fmt.Println("读取错误:", err)
return
}
fmt.Println("读取完成")
Split 方法
定义:
func (s *Scanner) Split(split SplitFunc)
说明:
- 设置分词函数
- 默认使用 ScanLines(按行分割)
- 可以自定义分词规则
- 必须在 Scan() 之前调用
示例:
// 按单词分割
scanner := bufio.NewScanner(strings.NewReader("go is fun"))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 输出:go, is, fun
Buffer 方法
定义:
func (s *Scanner) Buffer(buf []byte, max int)
说明:
- 设置缓冲区和最大 token 大小
- 默认最大 64KB(MaxScanTokenSize)
- 读取超长行时必须扩大
- 必须在 Scan() 之前调用
示例:
scanner := bufio.NewScanner(file)
// 扩大缓冲区到 1MB
buf := make([]byte, 0, 64*1024)
scanner.Buffer(buf, 1024*1024)
for scanner.Scan() {
line := scanner.Text()
// 处理超长行...
}
综合示例:
file, _ := os.Open("data.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fmt.Println(line)
}
if err := scanner.Err(); err != nil {
fmt.Println("读取错误:", err)
}
示例 2:按单词分割:
scanner := bufio.NewScanner(strings.NewReader("go is awesome"))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println("单词:", scanner.Text())
}
// 输出:
// 单词:go
// 单词:is
// 单词:awesome
示例 3:按字符分割:
scanner := bufio.NewScanner(strings.NewReader("你好"))
scanner.Split(bufio.ScanRunes)
for scanner.Scan() {
fmt.Println("字符:", scanner.Text())
}
// 输出:
// 字符:你
// 字符:好
示例 4:读取超长行:
file, _ := os.Open("large.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
// 扩大缓冲区到 1MB
buf := make([]byte, 0, 64*1024)
scanner.Buffer(buf, 1024*1024)
for scanner.Scan() {
line := scanner.Text()
// 处理超长行...
}
示例 5:自定义分词(CSV 解析):
data := "apple,banana,cherry"
// 自定义分词函数
commaSplit := func(data []byte, atEOF bool) (int, []byte, error) {
for i := 0; i < len(data); i++ {
if data[i] == ',' {
return i + 1, data[:i], nil
}
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
}
scanner := bufio.NewScanner(strings.NewReader(data))
scanner.Split(commaSplit)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 输出:apple, banana, cherry
示例 6:统计行数:
file, _ := os.Open("data.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
lines := 0
for scanner.Scan() {
lines++
}
if err := scanner.Err(); err != nil {
fmt.Println("读取失败:", err)
return
}
fmt.Println("总行数:", lines)
示例 7:保存所有行:
scanner := bufio.NewScanner(file)
var allLines []string
for scanner.Scan() {
// Text() 返回的字符串已复制,可以安全保存
allLines = append(allLines, scanner.Text())
}
注意事项:
❌ 忘记检查错误:
for scanner.Scan() {
process(scanner.Text())
}
// 忘记检查 Err()!
✅ 正确做法:
for scanner.Scan() {
process(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("错误:", err)
}
⚠️ Token 长度限制:
- 默认最大 64KB(
MaxScanTokenSize) - 超过会返回
ErrTooLong错误 - 使用
Buffer()方法扩大限制
⚠️ Text() 返回值生命周期:
Text()返回的字符串在下一次Scan()后会失效- 需要长期保存时应复制到切片或变量
读写组合器
ReadWriter
定义:
type ReadWriter struct {
*Reader
*Writer
}
说明:
- 组合了
*Reader和*Writer - 同时提供读取和写入功能
- 适合需要双向通信的场景
主要方法:
- 继承
Reader的所有方法(Read、ReadString等) - 继承
Writer的所有方法(Write、WriteString、Flush等)
示例:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
rw := bufio.NewReadWriter(
bufio.NewReader(os.Stdin),
bufio.NewWriter(os.Stdout),
)
fmt.Print("输入:")
text, _ := rw.ReadString('\n')
rw.WriteString("输出:" + text)
rw.Flush()
}
五、分词函数类型
Scanner 分词函数
SplitFunc
定义:
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
参数说明:
data []byte:当前缓冲区的数据atEOF bool:是否已到达输入末尾advance int:已处理的字节数(下次读取从此位置开始)token []byte:返回的 tokenerr error:错误(可使用ErrFinalToken表示结束)
内置分词函数:
| 函数 | 说明 | 示例 |
|---|---|---|
ScanLines | 按行分割(默认) | scanner.Split(ScanLines) |
ScanWords | 按单词分割 | scanner.Split(ScanWords) |
ScanBytes | 按字节分割 | scanner.Split(ScanBytes) |
ScanRunes | 按 rune 分割 | scanner.Split(ScanRunes) |
示例 1:ScanLines(默认):
scanner := bufio.NewScanner(strings.NewReader("line1\nline2\nline3"))
// 默认使用 ScanLines
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 输出:line1, line2, line3
示例 2:ScanWords:
scanner := bufio.NewScanner(strings.NewReader("go is fun"))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 输出:go, is, fun
示例 3:ScanBytes:
scanner := bufio.NewScanner(strings.NewReader("abc"))
scanner.Split(bufio.ScanBytes)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 输出:a, b, c
示例 4:ScanRunes:
scanner := bufio.NewScanner(strings.NewReader("你好"))
scanner.Split(bufio.ScanRunes)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 输出:你,好
示例 5:自定义分词(按逗号分割):
data := "a,b,c,d"
commaSplit := func(data []byte, atEOF bool) (int, []byte, error) {
for i := 0; i < len(data); i++ {
if data[i] == ',' {
return i + 1, data[:i], nil
}
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
}
scanner := bufio.NewScanner(strings.NewReader(data))
scanner.Split(commaSplit)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 输出:a, b, c, d
示例 6:自定义分词(固定长度):
// 每次读取 4 个字节
fixedSplit := func(data []byte, atEOF bool) (int, []byte, error) {
if len(data) >= 4 {
return 4, data[:4], nil
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
}
scanner := bufio.NewScanner(strings.NewReader("0123456789"))
scanner.Split(fixedSplit)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 输出:0123, 4567, 89
实现 SplitFunc 的规则:
- 返回 0, nil, nil:表示需要更多数据
- 返回 advance, token, nil:表示成功分词
- 返回 advance, token, err:表示错误(包括
ErrFinalToken) - advance 不能为负数
- advance 不能超出 data 长度
六、快速参考
错误变量
| 错误 | 说明 |
|---|---|
ErrBufferFull | 缓冲区已满 |
ErrTooLong | Token 超长 |
ErrInvalidUnreadByte | 非法的 UnreadByte |
ErrInvalidUnreadRune | 非法的 UnreadRune |
ErrAdvanceTooFar | Advance 超出范围 |
ErrNegativeAdvance | Advance 为负数 |
ErrNegativeCount | 读取计数为负数 |
ErrBadReadCount | 读取计数异常 |
ErrFinalToken | Scanner 分词结束标记 |
构造函数
| 函数 | 说明 |
|---|---|
NewReader(rd) | 创建默认缓冲 Reader |
NewReaderSize(rd, size) | 创建指定大小 Reader |
NewWriter(wr) | 创建默认缓冲 Writer |
NewWriterSize(wr, size) | 创建指定大小 Writer |
NewScanner(r) | 创建 Scanner |
NewReadWriter(r, w) | 创建读写组合 |
核心类型
| 类型 | 说明 |
|---|---|
Reader | 缓冲读取器 |
Writer | 缓冲写入器(必须 Flush) |
Scanner | 逐 token 读取器 |
ReadWriter | 读写组合器 |
分词函数
| 函数 | 说明 |
|---|---|
ScanLines | 按行分割(默认) |
ScanWords | 按单词分割 |
ScanBytes | 按字节分割 |
ScanRunes | 按 rune 分割 |
常量
| 常量 | 值 | 说明 |
|---|---|---|
MaxScanTokenSize | 65536 | Scanner 默认最大 token 大小 |
最后更新:2026-04-24
Go 版本:Go 1.0+ 🟢