Go 语言完全指南 / 17 - I/O:io.Reader/Writer、bufio、文件操作
17 - I/O
17.1 io.Reader 和 io.Writer
// io 包的核心接口
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
| 组合接口 | 说明 |
|---|---|
io.ReadWriter | 同时读写 |
io.ReadCloser | 读取并关闭 |
io.WriteCloser | 写入并关闭 |
io.ReadWriteCloser | 读写并关闭 |
io.ReadSeeker | 读取并定位 |
io.Seeker | 定位(Seek) |
package main
import (
"bytes"
"fmt"
"io"
"strings"
)
func main() {
// strings.Reader 实现 io.Reader
r1 := strings.NewReader("Hello, World!")
buf := make([]byte, 5)
n, err := r1.Read(buf)
fmt.Printf("读取 %d 字节: %s\n", n, buf[:n])
// bytes.Buffer 同时实现 io.Reader 和 io.Writer
var buf2 bytes.Buffer
buf2.WriteString("你好世界")
fmt.Println(buf2.String())
// 通用的复制函数
src := strings.NewReader("Go 语言真棒!")
var dst bytes.Buffer
n2, err := io.Copy(&dst, src)
fmt.Printf("复制 %d 字节: %s\n", n2, dst.String())
// 读取所有数据
r2 := strings.NewReader("完整内容")
data, _ := io.ReadAll(r2)
fmt.Println(string(data))
}
自定义 Reader/Writer
// 自定义 Reader:将读取的内容转大写
type UpperReader struct {
r io.Reader
}
func (ur *UpperReader) Read(p []byte) (int, error) {
n, err := ur.r.Read(p)
for i := 0; i < n; i++ {
if p[i] >= 'a' && p[i] <= 'z' {
p[i] -= 32
}
}
return n, err
}
// 自定义 Writer:带前缀输出
type PrefixWriter struct {
prefix string
w io.Writer
}
func (pw *PrefixWriter) Write(p []byte) (int, error) {
_, err := pw.w.Write([]byte(pw.prefix))
if err != nil {
return 0, err
}
return pw.w.Write(p)
}
func main() {
// 使用自定义 Reader
r := &UpperReader{r: strings.NewReader("hello world")}
data, _ := io.ReadAll(r)
fmt.Println(string(data)) // HELLO WORLD
// 使用自定义 Writer
pw := &PrefixWriter{prefix: "[LOG] ", w: os.Stdout}
fmt.Fprintln(pw, "这是一条日志")
}
17.2 bufio 包
缓冲 I/O,减少系统调用次数。
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// bufio.Reader
r := bufio.NewReader(strings.NewReader("Hello\nWorld\nGo\n"))
// 按行读取
for {
line, err := r.ReadString('\n')
if err != nil {
break
}
fmt.Print("行:", line)
}
// bufio.Scanner(更方便的按行读取)
scanner := bufio.NewScanner(strings.NewReader("Hello\nWorld\nGo"))
for scanner.Scan() {
fmt.Println("扫描:", scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("扫描错误:", err)
}
// bufio.Writer
var buf strings.Builder
w := bufio.NewWriter(&buf)
for i := 0; i < 5; i++ {
fmt.Fprintf(w, "Line %d\n", i)
}
w.Flush() // 必须调用 Flush 将缓冲数据写入底层 Writer
fmt.Print(buf.String())
}
从 stdin 读取输入
func main() {
scanner := bufio.NewScanner(os.Stdin)
fmt.Println("请输入你的名字:")
if scanner.Scan() {
name := scanner.Text()
fmt.Printf("你好, %s!\n", name)
}
}
Scanner 自定义分割
// 按单词分割
scanner := bufio.NewScanner(strings.NewReader("Hello World Go"))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 自定义缓冲区大小
scanner := bufio.NewScanner(os.Stdin)
scanner.Buffer(make([]byte, 1024), 1024*1024) // 最大 1MB
17.3 文件操作
package main
import (
"fmt"
"io"
"os"
"path/filepath"
)
func main() {
// 写入文件
err := os.WriteFile("test.txt", []byte("Hello, Go!"), 0644)
if err != nil {
panic(err)
}
defer os.Remove("test.txt")
// 读取文件
data, err := os.ReadFile("test.txt")
if err != nil {
panic(err)
}
fmt.Println(string(data))
// 低级文件操作
f, err := os.Create("test2.txt")
if err != nil {
panic(err)
}
defer f.Close()
defer os.Remove("test2.txt")
// 写入
f.WriteString("Hello, ")
f.Write([]byte("World!"))
// 读取
f2, _ := os.Open("test2.txt")
content, _ := io.ReadAll(f2)
fmt.Println(string(content))
f2.Close()
}
文件信息
func main() {
info, err := os.Stat("test.txt")
if err != nil {
if os.IsNotExist(err) {
fmt.Println("文件不存在")
}
return
}
fmt.Println("文件名:", info.Name())
fmt.Println("大小:", info.Size(), "bytes")
fmt.Println("权限:", info.Mode())
fmt.Println("修改时间:", info.ModTime())
fmt.Println("是否目录:", info.IsDir())
}
目录操作
func main() {
// 创建目录
os.MkdirAll("a/b/c", 0755)
defer os.RemoveAll("a")
// 读取目录
entries, _ := os.ReadDir(".")
for _, entry := range entries {
fmt.Printf("%s (dir=%v)\n", entry.Name(), entry.IsDir())
}
// 遍历目录树
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fmt.Println(path, info.Size())
return nil
})
}
临时文件
func main() {
// 创建临时文件
tmpFile, err := os.CreateTemp("", "example-*.txt")
if err != nil {
panic(err)
}
defer os.Remove(tmpFile.Name())
fmt.Println("临时文件:", tmpFile.Name())
tmpFile.WriteString("临时内容")
tmpFile.Close()
// 创建临时目录
tmpDir, err := os.MkdirTemp("", "example-")
if err != nil {
panic(err)
}
defer os.RemoveAll(tmpDir)
fmt.Println("临时目录:", tmpDir)
}
17.4 io 包实用函数
import (
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, World!")
// 有限读取
limited := io.LimitReader(r, 5)
data, _ := io.ReadAll(limited)
fmt.Println(string(data)) // Hello
// 节流 Reader(读取不超过 N 字节)
r2 := strings.NewReader("Long content here")
lr := io.LimitReader(r2, 4)
// 多路复用(TeeReader:读取的同时写入)
src := strings.NewReader("Hello")
var buf bytes.Buffer
tee := io.TeeReader(src, &buf)
data2, _ := io.ReadAll(tee)
fmt.Println("读取:", string(data2))
fmt.Println("复制:", buf.String())
// MultiReader:合并多个 Reader
r3 := io.MultiReader(
strings.NewReader("Hello "),
strings.NewReader("World"),
)
data3, _ := io.ReadAll(r3)
fmt.Println(string(data3))
// MultiWriter:写入多个 Writer
var buf1, buf2 bytes.Buffer
mw := io.MultiWriter(&buf1, &buf2)
mw.Write([]byte("Hello"))
fmt.Println(buf1.String(), buf2.String()) // Hello Hello
}
17.5 pipes
func main() {
// io.Pipe:同步管道
pr, pw := io.Pipe()
go func() {
fmt.Fprintln(pw, "Hello from writer")
pw.Close()
}()
data, _ := io.ReadAll(pr)
fmt.Println(string(data))
// os.Pipe:操作系统管道
r, w, _ := os.Pipe()
w.WriteString("OS pipe data")
w.Close()
data2, _ := io.ReadAll(r)
fmt.Println(string(data2))
}
🏢 业务场景
- 日志处理:bufio.Writer 缓冲写入日志文件
- 配置解析:os.ReadFile 读取 YAML/JSON 配置
- 文件上传:io.Copy 将请求体写入文件
- 数据管道:io.Pipe 连接生产者和消费者
- 批量处理:bufio.Scanner 逐行处理大文件