目录
- [🔵 Go 入门到精通:文件操作 ------ 从读写到遍历的完整指南](#🔵 Go 入门到精通:文件操作 —— 从读写到遍历的完整指南)
-
- [1. 开篇:Go 文件操作的哲学](#1. 开篇:Go 文件操作的哲学)
- [2. os 包:文件与目录的"操作系统"](#2. os 包:文件与目录的"操作系统")
-
- [2.1 打开与创建文件](#2.1 打开与创建文件)
- [2.2 目录操作](#2.2 目录操作)
- [2.3 文件信息 os.Stat](#2.3 文件信息 os.Stat)
- [3. 文件打开模式:位掩码的艺术](#3. 文件打开模式:位掩码的艺术)
-
- [3.1 常用模式组合](#3.1 常用模式组合)
- [3.2 实战示例](#3.2 实战示例)
- [4. io 包:一切 I/O 的抽象](#4. io 包:一切 I/O 的抽象)
-
- [4.1 理念:组合 > 继承](#4.1 理念:组合 > 继承)
- [5. io.Reader 与 io.Writer 接口](#5. io.Reader 与 io.Writer 接口)
-
- [5.1 文件读写基础](#5.1 文件读写基础)
- [5.2 Reader 的契约](#5.2 Reader 的契约)
- [6. io.Copy / io.ReadAll / io.TeeReader](#6. io.Copy / io.ReadAll / io.TeeReader)
-
- [6.1 io.Copy:数据搬运工](#6.1 io.Copy:数据搬运工)
- [6.2 io.ReadAll:一口气读完](#6.2 io.ReadAll:一口气读完)
- [6.3 io.TeeReader:数据分流](#6.3 io.TeeReader:数据分流)
- [6.4 io 包工具箱速查](#6.4 io 包工具箱速查)
- [7. bufio 包:带缓冲的高效读写](#7. bufio 包:带缓冲的高效读写)
-
- [7.1 为什么需要缓冲?](#7.1 为什么需要缓冲?)
- [7.2 bufio.Reader](#7.2 bufio.Reader)
- [7.3 bufio.Scanner:逐行读取最佳实践](#7.3 bufio.Scanner:逐行读取最佳实践)
- [7.4 bufio.Writer](#7.4 bufio.Writer)
- [7.5 Scanner vs Reader 对比](#7.5 Scanner vs Reader 对比)
- [8. ioutil 已废弃:用 os.ReadFile / os.WriteFile 替代](#8. ioutil 已废弃:用 os.ReadFile / os.WriteFile 替代)
-
- [8.1 迁移指南](#8.1 迁移指南)
- [8.2 新 API 使用](#8.2 新 API 使用)
- [9. path/filepath 包:跨平台路径操作](#9. path/filepath 包:跨平台路径操作)
-
- [9.1 路径拼接与分割](#9.1 路径拼接与分割)
- [9.2 路径清理与判断](#9.2 路径清理与判断)
- [9.3 通配符匹配](#9.3 通配符匹配)
- [10. os.FileInfo 与文件属性](#10. os.FileInfo 与文件属性)
- [11. 临时文件与目录](#11. 临时文件与目录)
- [12. 文件权限 os.FileMode](#12. 文件权限 os.FileMode)
-
- [12.1 实战:设置文件权限](#12.1 实战:设置文件权限)
- [12.2 权限速查表](#12.2 权限速查表)
- [13. 实战:递归目录遍历工具](#13. 实战:递归目录遍历工具)
- [14. 小结与互动](#14. 小结与互动)
-
- 核心要点回顾
- [📝 思考题](#📝 思考题)
- [💬 互动](#💬 互动)
🔵 Go 入门到精通:文件操作 ------ 从读写到遍历的完整指南
📅 更新于 2026年7月 | ✍️ 原创文章,转载请注明出处
1. 开篇:Go 文件操作的哲学
Go 的文件操作设计优雅而克制。与许多语言不同,Go 没有庞大的"一切皆文件"框架,而是通过组合小接口来构建强大的 I/O 能力:
┌─────────────────────────────────────────────┐
│ 你的业务代码 │
├─────────────────────────────────────────────┤
│ bufio(缓冲读写、Scanner 逐行读取) │
├─────────────────────────────────────────────┤
│ io(Reader/Writer 接口、Copy/ReadAll/Pipe) │
├─────────────────────────────────────────────┤
│ os(Open/Create/Stat/Remove/Rename/Mkdir) │
├─────────────────────────────────────────────┤
│ syscall(系统调用层,通常不直接使用) │
└─────────────────────────────────────────────┘
这篇文章将带你从底层到高层、从 os 到 bufio,完整掌握 Go 的文件操作。
2. os 包:文件与目录的"操作系统"
2.1 打开与创建文件
go
package main
import (
"fmt"
"os"
)
func main() {
// 1️⃣ os.Open:只读打开
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("打开失败:", err)
return
}
defer file.Close() // ⭐ 务必关闭
// 2️⃣ os.Create:创建或截断(覆盖)
f2, err := os.Create("output.txt")
if err != nil {
panic(err)
}
defer f2.Close()
f2.WriteString("Hello, Go!\n")
// 3️⃣ os.OpenFile:完全控制(最灵活)
f3, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer f3.Close()
f3.WriteString("追加一行日志\n")
}
2.2 目录操作
go
func main() {
// 创建目录
os.Mkdir("mydir", 0755) // 创建单级目录
os.MkdirAll("a/b/c/d", 0755) // 创建多级目录(类似 mkdir -p)
// 获取当前工作目录
cwd, _ := os.Getwd()
fmt.Println("当前目录:", cwd)
// 切换目录
os.Chdir("/tmp")
// 删除
os.Remove("output.txt") // 删除文件
os.RemoveAll("mydir") // 递归删除目录
// 重命名(也可用于移动)
os.Rename("old.txt", "new.txt")
// 读取目录内容
entries, _ := os.ReadDir(".")
for _, entry := range entries {
fmt.Printf(" %s (是目录: %v)\n", entry.Name(), entry.IsDir())
}
}
2.3 文件信息 os.Stat
go
func main() {
info, err := os.Stat("example.txt")
if err != nil {
if os.IsNotExist(err) {
fmt.Println("文件不存在")
}
return
}
fmt.Println("文件名:", info.Name())
fmt.Println("大小:", info.Size(), "字节")
fmt.Println("权限:", info.Mode())
fmt.Println("修改时间:", info.ModTime())
fmt.Println("是目录:", info.IsDir())
fmt.Println("系统信息:", info.Sys()) // 底层系统相关
}
3. 文件打开模式:位掩码的艺术
Go 使用位掩码(bitmask)来组合多个打开模式:
go
const (
O_RDONLY int = syscall.O_RDONLY // 只读
O_WRONLY int = syscall.O_WRONLY // 只写
O_RDWR int = syscall.O_RDWR // 读写
O_APPEND int = syscall.O_APPEND // 追加
O_CREATE int = syscall.O_CREATE // 不存在则创建
O_EXCL int = syscall.O_EXCL // 与 O_CREATE 配合,文件存在则失败
O_SYNC int = syscall.O_SYNC // 同步写入
O_TRUNC int = syscall.O_TRUNC // 打开时清空文件
)
3.1 常用模式组合
| 组合 | 含义 | 使用场景 |
|---|---|---|
O_RDONLY |
只读 | os.Open() 内部使用 |
| `O_WRONLY | O_CREATE | O_TRUNC` |
| `O_WRONLY | O_CREATE | O_APPEND` |
| `O_RDWR | O_CREATE` | 读写,不存在则创建 |
| `O_RDWR | O_CREATE | O_EXCL` |
3.2 实战示例
go
// 日志文件:追加模式
func openLogFile(path string) (*os.File, error) {
return os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
}
// 锁文件:原子创建(存在即失败)
func createLockFile(path string) (*os.File, error) {
return os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
}
// 配置文件:读写,保留原有内容
func openConfigFile(path string) (*os.File, error) {
return os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
}
4. io 包:一切 I/O 的抽象
io 包是 Go I/O 的基石,它通过 Reader 和 Writer 两个核心接口统一了所有数据源和数据汇。
4.1 理念:组合 > 继承
go
// 一切数据源都是 Reader
type Reader interface {
Read(p []byte) (n int, err error)
}
// 一切数据汇都是 Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
// Closer:需要关闭的资源
type Closer interface {
Close() error
}
// 更多组合接口
type ReadWriter interface {
Reader
Writer
}
type ReadCloser interface {
Reader
Closer
}
type ReadWriteCloser interface {
Reader
Writer
Closer
}
这意味着:文件、网络连接、HTTP 响应体、压缩流、加密流......它们都是 io.Reader。你可以用同样的代码处理完全不同的数据源。
5. io.Reader 与 io.Writer 接口
5.1 文件读写基础
go
package main
import (
"fmt"
"io"
"os"
)
func main() {
f, _ := os.Open("example.txt")
defer f.Close()
// 方式1:固定大小读取
buf := make([]byte, 1024)
for {
n, err := f.Read(buf)
if err == io.EOF {
break // 读取完毕
}
if err != nil {
panic(err)
}
fmt.Print(string(buf[:n]))
}
// 方式2:写入
fw, _ := os.Create("copy.txt")
defer fw.Close()
fw.Write([]byte("Hello, Go!\n"))
fw.WriteString("第二行内容\n")
}
5.2 Reader 的契约
go
// Read 方法的行为规范:
// 1. 最多读取 len(p) 字节到 p
// 2. 返回实际读取的字节数 n(0 <= n <= len(p))
// 3. 可能同时返回 n > 0 和 err(这是合法的!)
// 4. 返回 io.EOF 表示流结束(可能同时返回数据)
func readFull(r io.Reader, buf []byte) (int, error) {
total := 0
for total < len(buf) {
n, err := r.Read(buf[total:])
total += n
if err == io.EOF {
return total, io.EOF
}
if err != nil {
return total, err
}
}
return total, nil
}
6. io.Copy / io.ReadAll / io.TeeReader
6.1 io.Copy:数据搬运工
go
func main() {
// 文件复制(最简洁的方式)
src, _ := os.Open("source.txt")
defer src.Close()
dst, _ := os.Create("dest.txt")
defer dst.Close()
written, err := io.Copy(dst, src) // 内部用 32KB 缓冲区
fmt.Printf("复制了 %d 字节\n", written)
// io.CopyBuffer:自定义缓冲区大小
buf := make([]byte, 1024*1024) // 1MB 缓冲区
io.CopyBuffer(dst, src, buf)
// io.CopyN:限制复制字节数
io.CopyN(dst, src, 1024) // 最多复制 1KB
}
6.2 io.ReadAll:一口气读完
go
func main() {
// Go 1.16+ 推荐使用 io.ReadAll(替代 ioutil.ReadAll)
f, _ := os.Open("config.json")
defer f.Close()
data, err := io.ReadAll(f) // 读取全部内容到内存
if err != nil {
panic(err)
}
fmt.Println(string(data))
// ⚠️ 注意:ReadAll 会把整个文件加载到内存
// 大文件请使用 bufio.Scanner 或分块读取
}
6.3 io.TeeReader:数据分流
go
func main() {
// TeeReader:读取数据的同时写到另一个 Writer(类似 Unix tee 命令)
src, _ := os.Open("input.txt")
defer src.Close()
logFile, _ := os.Create("read.log")
defer logFile.Close()
// 从 src 读取的数据会同时写入 logFile
tee := io.TeeReader(src, logFile)
// 处理 tee(同时完成了数据记录)
data, _ := io.ReadAll(tee)
fmt.Println("读取内容:", string(data))
// read.log 中也有一份完全相同的副本
}
6.4 io 包工具箱速查
| 函数 | 功能 | 适用场景 |
|---|---|---|
io.Copy(dst, src) |
数据搬运 | 文件复制、HTTP 下载 |
io.CopyN(dst, src, n) |
限字节搬运 | 限制大小的下载 |
io.ReadAll(r) |
全部读取 | 小文件/配置读取 |
io.TeeReader(r, w) |
分流读取 | 日志记录、数据审计 |
io.MultiReader(...) |
串联多个 Reader | 合并多个文件 |
io.MultiWriter(...) |
广播到多个 Writer | 同时写日志+控制台 |
io.Pipe() |
内存管道 | goroutine 间流式传输 |
io.LimitReader(r, n) |
限制读取长度 | 限制输入大小 |
7. bufio 包:带缓冲的高效读写
7.1 为什么需要缓冲?
每次 os.File.Read() 都会触发系统调用,频繁小量读取性能极差。bufio 在用户空间维护一个缓冲区,减少系统调用次数:
无缓冲: 每 1 字节 → 1 次系统调用 → 1000 字节 = 1000 次系统调用
有缓冲: 每 4096 字节 → 1 次系统调用 → 1000 字节 = 1 次系统调用
7.2 bufio.Reader
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
f, _ := os.Open("example.txt")
defer f.Close()
reader := bufio.NewReader(f) // 默认 4KB 缓冲区
// reader := bufio.NewReaderSize(f, 64*1024) // 自定义 64KB
// ReadString:读到分隔符
line, err := reader.ReadString('\n')
fmt.Println("第一行:", line)
// ReadLine:低级 API,不包含 \n
// ReadBytes:读直到分隔符(返回 []byte)
// Peek:预览而不消费
preview, _ := reader.Peek(10)
fmt.Println("前10字节:", string(preview))
}
7.3 bufio.Scanner:逐行读取最佳实践
go
func main() {
f, _ := os.Open("access.log")
defer f.Close()
scanner := bufio.NewScanner(f)
// 自定义分割函数(默认按行)
// scanner.Split(bufio.ScanWords) // 按单词
// scanner.Split(bufio.ScanRunes) // 按字符
// scanner.Split(bufio.ScanBytes) // 按字节
// 设置缓冲区大小(处理长行)
const maxScanTokenSize = 1024 * 1024 // 1MB
buf := make([]byte, maxScanTokenSize)
scanner.Buffer(buf, maxScanTokenSize)
lineNum := 0
for scanner.Scan() {
lineNum++
line := scanner.Text()
fmt.Printf("第%d行: %s\n", lineNum, line)
}
if err := scanner.Err(); err != nil {
fmt.Println("扫描出错:", err)
}
}
7.4 bufio.Writer
go
func main() {
f, _ := os.Create("output.txt")
defer f.Close()
writer := bufio.NewWriter(f) // 默认 4KB 缓冲区
writer.WriteString("Hello, ")
writer.WriteString("World!\n")
writer.WriteByte('!')
// ⚠️ 必须 Flush!否则缓冲区的数据不会写入文件
writer.Flush()
// 更好的做法:defer Flush
// defer writer.Flush()
}
7.5 Scanner vs Reader 对比
| 特性 | bufio.Scanner |
bufio.Reader |
|---|---|---|
| 使用复杂度 | ⭐ 简单 | ⭐⭐ 中等 |
| 逐行读取 | ✅ Scan() + Text() |
✅ ReadString('\n') |
| 长行支持 | 需手动调大 Buffer() |
✅ 自动处理 |
| 预览 | ❌ 不支持 | ✅ Peek() |
| 行号追踪 | ❌ 需手动计数 | ❌ 需手动计数 |
| 推荐场景 | 简单逐行读取 | 需要灵活控制的场景 |
8. ioutil 已废弃:用 os.ReadFile / os.WriteFile 替代
8.1 迁移指南
Go 1.16 起,io/ioutil 包被标记为废弃(Deprecated),所有函数迁移到 os 和 io 包:
| 废弃 API (ioutil) | 新 API | 功能 |
|---|---|---|
ioutil.ReadFile |
os.ReadFile |
读取整个文件 |
ioutil.WriteFile |
os.WriteFile |
写入整个文件 |
ioutil.ReadAll |
io.ReadAll |
读取全部 |
ioutil.ReadDir |
os.ReadDir |
读取目录 |
ioutil.TempFile |
os.CreateTemp |
创建临时文件 |
ioutil.TempDir |
os.MkdirTemp |
创建临时目录 |
ioutil.NopCloser |
io.NopCloser |
无操作 Closer |
8.2 新 API 使用
go
package main
import (
"fmt"
"os"
)
func main() {
// 读取整个文件(小文件适用)
data, err := os.ReadFile("config.json")
if err != nil {
panic(err)
}
fmt.Println(string(data))
// 写入整个文件(一次性写入)
content := []byte("Hello, Go 2026!\n")
err = os.WriteFile("hello.txt", content, 0644)
if err != nil {
panic(err)
}
// 追加模式
f, _ := os.OpenFile("hello.txt", os.O_APPEND|os.O_WRONLY, 0644)
defer f.Close()
f.WriteString("追加的内容\n")
}
⚠️
os.ReadFile和os.WriteFile只适合处理小文件 (KB 级别)。大文件(MB/GB 级别)请使用bufio或流式处理。
9. path/filepath 包:跨平台路径操作
9.1 路径拼接与分割
go
package main
import (
"fmt"
"path/filepath"
)
func main() {
// Join:智能路径拼接(自动处理分隔符)
path := filepath.Join("home", "user", "documents", "file.txt")
fmt.Println(path) // home/user/documents/file.txt (Linux)
// home\user\documents\file.txt (Windows)
// Dir / Base:获取目录和文件名
fmt.Println(filepath.Dir("/a/b/c.txt")) // /a/b
fmt.Println(filepath.Base("/a/b/c.txt")) // c.txt
// Ext:获取扩展名
fmt.Println(filepath.Ext("file.txt")) // .txt
fmt.Println(filepath.Ext("file")) // ""(无扩展名)
fmt.Println(filepath.Ext("file.tar.gz")) // .gz(只取最后一个)
// 去除扩展名
name := "file.tar.gz"
fmt.Println(name[:len(name)-len(filepath.Ext(name))]) // file.tar
// Split:分割目录与文件名
dir, file := filepath.Split("/a/b/c.txt")
fmt.Println(dir, file) // /a/b/ c.txt
}
9.2 路径清理与判断
go
// Clean:清理路径(消除 ../ 和多余分隔符)
fmt.Println(filepath.Clean("/a/b/../c/./d")) // /a/c/d
// Abs:获取绝对路径
abs, _ := filepath.Abs("./relative/path")
fmt.Println(abs) // /home/user/relative/path
// Rel:获取相对路径
rel, _ := filepath.Rel("/a/b", "/a/b/c/d")
fmt.Println(rel) // c/d
// IsAbs:判断是否绝对路径
fmt.Println(filepath.IsAbs("/usr/bin")) // true
fmt.Println(filepath.IsAbs("./config")) // false
9.3 通配符匹配
go
// Match:模式匹配
fmt.Println(filepath.Match("*.go", "main.go")) // true <nil>
fmt.Println(filepath.Match("???", "abc")) // true <nil>
fmt.Println(filepath.Match("data[0-9].json", "data1.json")) // true <nil>
// Glob:查找匹配的文件
matches, _ := filepath.Glob("*.go")
for _, m := range matches {
fmt.Println(m)
}
10. os.FileInfo 与文件属性
go
func inspectFile(path string) {
info, err := os.Stat(path)
if err != nil {
fmt.Println("错误:", err)
return
}
if info.IsDir() {
fmt.Println("📁", path, "(目录)")
} else {
fmt.Println("📄", path)
}
fmt.Printf(" ├─ 大小: %d 字节\n", info.Size())
fmt.Printf(" ├─ 权限: %s (%o)\n", info.Mode(), info.Mode().Perm())
fmt.Printf(" ├─ 修改时间: %s\n", info.ModTime().Format("2006-01-02 15:04:05"))
fmt.Printf(" └─ 系统信息: %T\n", info.Sys())
}
// 判断文件类型
func fileType(info os.FileInfo) string {
mode := info.Mode()
switch {
case mode.IsRegular():
return "普通文件"
case mode.IsDir():
return "目录"
case mode&os.ModeSymlink != 0:
return "符号链接"
case mode&os.ModeDevice != 0:
return "设备文件"
default:
return "未知类型"
}
}
11. 临时文件与目录
go
package main
import (
"fmt"
"os"
)
func main() {
// 创建临时文件
// 参数:(目录, 文件名前缀)
tmpFile, err := os.CreateTemp("", "go-prefix-*.txt")
if err != nil {
panic(err)
}
defer os.Remove(tmpFile.Name()) // 手动清理
defer tmpFile.Close()
fmt.Println("临时文件:", tmpFile.Name()) // /tmp/go-prefix-123456.txt
tmpFile.WriteString("临时数据")
tmpFile.Seek(0, 0)
data, _ := os.ReadFile(tmpFile.Name())
fmt.Println("内容:", string(data))
// 创建临时目录
tmpDir, err := os.MkdirTemp("", "go-tmpdir-*")
if err != nil {
panic(err)
}
defer os.RemoveAll(tmpDir) // 递归清理
fmt.Println("临时目录:", tmpDir)
}
| 函数 | 用途 | 清理方式 |
|---|---|---|
os.CreateTemp(dir, pattern) |
创建临时文件 | os.Remove(path) |
os.MkdirTemp(dir, pattern) |
创建临时目录 | os.RemoveAll(path) |
12. 文件权限 os.FileMode
os.FileMode 是 uint32 的类型别名,表示文件的模式和权限。
go
type FileMode uint32
const (
// 权限位(Unix 风格)
ModeDir FileMode = 1 << (32 - 1 - iota) // d: 目录
ModeAppend // a: 仅追加
ModeExclusive // l: 独占
ModeTemporary // T: 临时文件
ModeSymlink // L: 符号链接
ModeDevice // D: 设备文件
ModeNamedPipe // p: 命名管道(FIFO)
ModeSocket // S: Unix 域 socket
ModeSetuid // u: setuid
ModeSetgid // g: setgid
ModeCharDevice // c: Unix 字符设备
ModeSticky // t: sticky
ModeIrregular // ?: 非常规文件
)
// Permission 位(Unix 权限)
// rwx rwx rwx = 所有者 组 其他
// 0644 = rw-r--r--
// 0755 = rwxr-xr-x
12.1 实战:设置文件权限
go
func main() {
// 创建时指定权限
os.WriteFile("script.sh", []byte("#!/bin/bash\necho hello"), 0755)
// 权限 = rwxr-xr-x
// 修改已有文件权限
os.Chmod("script.sh", 0755)
// 修改所有者(Unix only)
// os.Chown("file.txt", uid, gid)
// 检查权限
info, _ := os.Stat("script.sh")
fmt.Printf("权限: %s (%o)\n", info.Mode().Perm(), info.Mode().Perm())
// 输出:权限: -rwxr-xr-x (755)
}
12.2 权限速查表
| 八进制 | 符号 | 含义 |
|---|---|---|
0400 |
r-------- |
所有者可读 |
0200 |
-w------- |
所有者可写 |
0100 |
--x------ |
所有者可执行 |
0644 |
rw-r--r-- |
所有者读写,组和其他只读(常用) |
0755 |
rwxr-xr-x |
所有者全权限,组和其他读执行(可执行文件) |
0600 |
rw------- |
仅所有者读写(敏感文件) |
0777 |
rwxrwxrwx |
所有人全权限( ⚠️ 慎用) |
13. 实战:递归目录遍历工具
go
package main
import (
"fmt"
"os"
"path/filepath"
)
// WalkDir 递归遍历目录(Go 1.16+ 高效版本)
func walkDirectory(root string) error {
return filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err // 访问出错则跳过
}
// 计算缩进
rel, _ := filepath.Rel(root, path)
depth := len(filepath.SplitList(rel))
indent := ""
for i := 0; i < depth; i++ {
indent += " "
}
info, _ := d.Info()
if d.IsDir() {
fmt.Printf("%s📁 %s/\n", indent, d.Name())
} else {
size := info.Size()
sizeStr := formatSize(size)
fmt.Printf("%s📄 %s (%s)\n", indent, d.Name(), sizeStr)
}
return nil
})
}
func formatSize(bytes int64) string {
const unit = 1024
if bytes < unit {
return fmt.Sprintf("%d B", bytes)
}
div, exp := int64(unit), 0
for n := bytes / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}
func main() {
if len(os.Args) < 2 {
fmt.Println("用法: go run main.go <目录>")
return
}
walkDirectory(os.Args[1])
}
14. 小结与互动
核心要点回顾
| 知识点 | 一句话总结 |
|---|---|
| os.Open / os.Create | 基本的文件打开/创建,defer Close() 是铁律 |
| os.OpenFile | 通过位掩码组合任意打开模式 |
| io.Reader / io.Writer | Go I/O 的万能抽象,统一了所有数据源 |
| io.Copy | 复制数据的最简洁方式(32KB 默认缓冲) |
| bufio.Scanner | 逐行读取的首选方案 |
| os.ReadFile / os.WriteFile | 替代废弃的 ioutil,适合小文件 |
| filepath.Join | 跨平台路径拼接,禁止手动拼 / |
| filepath.WalkDir | Go 1.16+ 的高效目录遍历 |
📝 思考题
io.Copy内部默认使用多大的缓冲区?为什么要用这个大小?- 如何用
io.TeeReader实现一个"读取文件的同时计算其 MD5 哈希"的功能? bufio.Scanner默认最大行长度是多少?如果日志文件单行超过这个长度会发生什么?
💬 互动
你在 Go 文件操作中踩过哪些坑?是用 ioutil 的老代码迁移了吗?欢迎在评论区分享你的经验和心得!
👨💻 作者 :布朗克168
📚 系列 :Go 入门到精通 2026
上一篇:时间与日期| 下一篇:Goroutine 入门
如果你觉得这篇文章对你有帮助,别忘了点赞 👍、收藏 ⭐ 和关注!