Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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)
}

新手注意事项

  1. ReadString('\n') - 读取到换行符为止,返回的字符串包含换行符
  2. writer.Flush() - 必须调用,否则数据会丢失
  3. scanner.Scan() - 在循环中使用,返回 false 时结束
  4. scanner.Text() - 获取当前行内容,不包含换行符
  5. 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

说明

  • 当缓冲区无法容纳更多数据时返回
  • 常见于 ReadSliceReadLine 方法
  • 可通过增大缓冲区或使用 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.Readerio.WriterToio.ByteReaderio.ByteScannerio.RuneReaderio.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.Writerio.ByteWriterio.StringWriterio.RuneWriterio.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 的所有方法(ReadReadString 等)
  • 继承 Writer 的所有方法(WriteWriteStringFlush 等)

示例

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:返回的 token
  • err 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 的规则

  1. 返回 0, nil, nil:表示需要更多数据
  2. 返回 advance, token, nil:表示成功分词
  3. 返回 advance, token, err:表示错误(包括 ErrFinalToken
  4. advance 不能为负数
  5. advance 不能超出 data 长度

六、快速参考

错误变量

错误说明
ErrBufferFull缓冲区已满
ErrTooLongToken 超长
ErrInvalidUnreadByte非法的 UnreadByte
ErrInvalidUnreadRune非法的 UnreadRune
ErrAdvanceTooFarAdvance 超出范围
ErrNegativeAdvanceAdvance 为负数
ErrNegativeCount读取计数为负数
ErrBadReadCount读取计数异常
ErrFinalTokenScanner 分词结束标记

构造函数

函数说明
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 分割

常量

常量说明
MaxScanTokenSize65536Scanner 默认最大 token 大小

最后更新:2026-04-24
Go 版本:Go 1.0+ 🟢