io.Reader
和 io.Writer
接口介绍在 Go 语言中,io
包定义了两个最基础和最重要的接口:io.Reader
和 io.Writer
。它们是 Go 语言中进行输入/输出操作的核心抽象,实现了极大的灵活性和可组合性。
io.Reader
接口io.Reader
接口定义了一个 Read
方法:
type Reader interface {
Read(p []byte) (n int, err error)
}
Read(p []byte) (n int, err error)
:
p
中。n
是实际读取的字节数。err
是读取过程中可能发生的错误。
Read
通常返回 n > 0
且 err == io.EOF
,或者在后续调用中返回 n == 0
且 err == io.EOF
。Read
返回 n < len(p)
但没有错误时,表示部分读取,即数据源没有足够的数据填充 p
。Read
返回 n == 0
且 err == nil
,表示目前没有数据可用,但这不是错误,调用者应再次尝试读取。io.Reader
的作用:
Read
方法的类型都可以被视为一个数据源。这包括文件 (*os.File
)、网络连接 (net.Conn
)、内存中的字节切片 (bytes.Reader
)、字符串 (strings.Reader
),甚至加密解密流、压缩解压缩流等。Reader
接口,它们可以无缝地相互连接和传递。例如,你可以用 bufio.NewReader
封装一个 *os.File
,也可以封装一个 bytes.Reader
。Read
方法的参数是一个字节切片,它鼓励分块读取和处理数据流,而不是一次性加载所有数据到内存,这对于处理大文件或无限数据流非常高效。常见实现者:*os.File
, *net.TCPConn
, *bytes.Buffer
, *bytes.Reader
, *strings.Reader
, *bufio.Reader
等。
io.Writer
接口io.Writer
接口定义了一个 Write
方法:
type Writer interface {
Write(p []byte) (n int, err error)
}
Write(p []byte) (n int, err error)
:
p
中的数据写入到输出目标。n
是实际写入的字节数。err
是写入过程中可能发生的错误。
Write
返回 n < len(p)
但 err == nil
,通常表示部分写入,即目标无法一次性接受所有数据。Write
返回 n > 0
但 err != nil
,表示在写入部分数据后发生了错误。io.Writer
的作用:
Write
方法的类型都可以被视为一个数据目标。这包括文件 (*os.File
)、网络连接 (net.Conn
)、内存中的字节缓冲区 (bytes.Buffer
)、os.Stdout
等。Writer
接口的类型都可以互换使用。你可以将数据写入到 *os.File
,也可以通过 bufio.NewWriter
写入到 *os.File
。Reader
类似,Writer
也支持分块写入,适用于处理大数据。常见实现者:*os.File
, *net.TCPConn
, *bytes.Buffer
, os.Stdout
, os.Stderr
, *bufio.Writer
等。
io.Reader
和 io.Writer
是 Go 语言 I/O 设计的基石,它们通过简单的接口定义实现了强大的抽象和可组合性,使得 Go 语言的 I/O 操作既高效又灵活。bufio
包正是基于这两个接口之上构建的,通过引入缓冲区进一步优化了性能。
在 Go 语言中,bufio
包提供了缓冲 I/O 操作的功能。它封装了 io.Reader
或 io.Writer
对象,创建了另一个同样实现这些接口但提供了缓冲以及一些文本 I/O 辅助功能的 Reader 或 Writer 对象。
直接对底层 I/O 对象(例如文件或网络连接)进行每次读写操作可能会非常低效。每次系统调用都会产生开销。通过使用缓冲,bufio
包可以在内存中积累数据,然后一次性进行更大块的读写操作,从而减少系统调用的次数,提高 I/O 性能。
bufio
包的主要类型和功能:bufio.Reader
:
io.Reader
提供缓冲读取功能。bufio.NewReader(rd io.Reader)
:创建一个带有默认缓冲区大小(通常为 4096 字节)的 Reader
。bufio.NewReaderSize(rd io.Reader, size int)
:创建一个指定缓冲区大小的 Reader
。Read(p []byte) (n int, err error)
:从输入中读取最多 len(p)
字节到 p
中。ReadByte() (byte, error)
:读取并返回下一个字节。ReadRune() (r rune, size int, err error)
:读取并返回下一个 UTF-8 编码的 Unicode 字符(rune)。ReadLine() (line []byte, isPrefix bool, err error)
:读取一行数据,直到遇到换行符。如果一行太长,isPrefix
会为 true
。ReadString(delim byte) (string, error)
:读取直到遇到指定的分隔符,并返回读取到的字符串(包含分隔符)。ReadBytes(delim byte) ([]byte, error)
:与 ReadString
类似,但返回字节切片。Peek(n int) ([]byte, error)
:查看接下来的 n
字节,但不会消耗它们(下一次读取仍然会读到这些字节)。Buffered() int
:返回当前缓冲区中可读取的字节数。Size() int
:返回底层缓冲区的大小。bufio.Writer
:
io.Writer
提供缓冲写入功能。bufio.NewWriter(wr io.Writer)
:创建一个带有默认缓冲区大小的 Writer
。bufio.NewWriterSize(wr io.Writer, size int)
:创建一个指定缓冲区大小的 Writer
。Write(p []byte) (n int, err error)
:将 p
中的数据写入缓冲区。WriteByte(c byte) error
:写入一个字节到缓冲区。WriteRune(r rune) (size int, err error)
:写入一个 Unicode 字符到缓冲区。WriteString(s string) (n int, err error)
:写入一个字符串到缓冲区。Flush() error
:将缓冲区中的所有数据写入到底层 io.Writer
。这是非常重要的方法,如果忘记调用,数据可能不会被写入!Available() int
:返回缓冲区中可用的字节空间。Buffered() int
:返回缓冲区中已填充的字节数。Size() int
:返回底层缓冲区的大小。bufio.Scanner
:
bufio.NewScanner(r io.Reader)
:创建一个新的 Scanner
。Scan() bool
:推进扫描器到下一个 token(默认是下一行),成功返回 true
,遇到文件末尾或错误返回 false
。Text() string
:返回当前 token 的文本内容。Bytes() []byte
:返回当前 token 的字节切片内容。Err() error
:返回 Scan
过程中遇到的任何非 EOF 错误。Split(splitFunc SplitFunc)
:设置扫描器的分割函数。bufio
包提供了几个预定义的分割函数:
bufio.ScanLines
:按行分割(默认)。bufio.ScanWords
:按单词分割。bufio.ScanBytes
:按字节分割。bufio.ScanRunes
:按 UTF-8 字符分割。SplitFunc
来实现更复杂的分割逻辑。使用 bufio.Reader
读取文件:
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
// 逐行读取
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
fmt.Print(line) // 打印最后一行(可能没有换行符)
break
}
fmt.Println("Error reading file:", err)
return
}
fmt.Print(line)
}
fmt.Println("\n--- 读取特定字节 ---")
// 重新打开文件或Seek到开头进行第二次读取
file.Seek(0, io.SeekStart)
reader.Reset(file) // 重置Reader,使用新的底层io.Reader
b, err := reader.ReadByte()
if err != nil {
fmt.Println("Error reading byte:", err)
return
}
fmt.Printf("第一个字节: %c\n", b)
// Peek
peekedBytes, err := reader.Peek(5)
if err != nil {
fmt.Println("Error peeking:", err)
return
}
fmt.Printf("Peeked 5 bytes: %s\n", string(peekedBytes))
// 再次读取一个字节,会是Peeked之后第一个字节
b2, err := reader.ReadByte()
if err != nil {
fmt.Println("Error reading byte:", err)
return
}
fmt.Printf("第二个字节: %c\n", b2) // 会是Peeked之后第一个字节
}
使用 bufio.Writer
写入文件:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Create("output.txt")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
_, err = writer.WriteString("Hello, Go bufio!\n")
if err != nil {
fmt.Println("Error writing string:", err)
return
}
_, err = writer.WriteString("This is another line.\n")
if err != nil {
fmt.Println("Error writing string:", err)
return
}
fmt.Println("Buffer size:", writer.Size())
fmt.Println("Buffered bytes:", writer.Buffered())
fmt.Println("Available bytes in buffer:", writer.Available())
// !!!非常重要:调用 Flush() 将缓冲区中的数据写入文件
err = writer.Flush()
if err != nil {
fmt.Println("Error flushing writer:", err)
return
}
fmt.Println("数据已写入 output.txt")
}
使用 bufio.Scanner
逐行读取文件:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
content := `Line 1: Hello world
Line 2: Go programming
Line 3: bufio package`
// 从字符串读取(也可以从文件读取)
reader := strings.NewReader(content)
scanner := bufio.NewScanner(reader)
lineNum := 1
for scanner.Scan() { // 每次调用 Scan() 都会读取下一行
line := scanner.Text() // 获取当前行的文本
fmt.Printf("Line %d: %s\n", lineNum, line)
lineNum++
}
if err := scanner.Err(); err != nil {
fmt.Println("Error scanning:", err)
}
fmt.Println("\n--- 单词扫描 ---")
wordContent := "This is a sentence with several words."
wordReader := strings.NewReader(wordContent)
wordScanner := bufio.NewScanner(wordReader)
wordScanner.Split(bufio.ScanWords) // 设置为按单词分割
wordNum := 1
for wordScanner.Scan() {
word := wordScanner.Text()
fmt.Printf("Word %d: %s\n", wordNum, word)
wordNum++
}
if err := wordScanner.Err(); err != nil {
fmt.Println("Error scanning words:", err)
}
}
bufio
包是 Go 语言中进行高效 I/O 操作的重要组成部分。
bufio.Reader
可以提高读取效率,并提供方便的文本读取方法(如 ReadString
、ReadLine
)。bufio.Writer
可以提高写入效率,减少系统调用。切记在使用 Writer
后调用 Flush()
将缓冲区内容强制写入底层。bufio.Scanner
提供了一种非常方便且高效的方式来逐行、逐词或按自定义规则处理输入流,特别适合处理文本数据。