Go 文件处理
读写文件、bufio、目录遍历、以及大文件的分块处理。
Key Points小文件用 `os.ReadFile`,大文件用 `bufio.Scanner` 逐行读,写文件记得 `defer f.Close()`。
一次性读写
Go
// 写
content := []byte("第一行\n第二行\n")
err := os.WriteFile("demo.txt", content, 0644)
if err != nil {
fmt.Println("写失败", err)
}
// 读
data, err := os.ReadFile("demo.txt")
if err != nil {
fmt.Println("读失败", err)
}
fmt.Print(string(data))
fmt.Println("字节数", len(data))Output
第一行
第二行
字节数 20「第一行\n」是 10 字节(三个汉字 9 + 换行 1),两行共 20。os.ReadFile 会把整个文件读进内存,只适合小文件。
打开与关闭
Go
f, err := os.Open("demo.txt") // 只读
if err != nil {
return err
}
defer f.Close() // 必须
// 可写、追加、创建
f2, err := os.OpenFile("log.txt",
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return err
}
defer f2.Close()
f2.WriteString("新日志\n")| Flag | Meaning |
|---|---|
os.O_RDONLY | 只读 |
os.O_WRONLY | 只写 |
os.O_RDWR | 读写 |
os.O_CREATE | 不存在就创建 |
os.O_TRUNC | 打开时清空 |
os.O_APPEND | 追加 |
逐行读大文件
Go
f, err := os.Open("big.log")
if err != nil {
return err
}
defer f.Close()
scanner := bufio.NewScanner(f)
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024) // 提高单行上限
lineNo := 0
for scanner.Scan() {
lineNo++
if strings.Contains(scanner.Text(), "ERROR") {
fmt.Println(lineNo, scanner.Text())
}
}
if err := scanner.Err(); err != nil {
return err
}Output
42 2026-10-31 ERROR 连接超时
87 2026-10-31 ERROR 写入失败bufio.Scanner caps a line at 64 KB by default and stops silently with token too long. If your logs contain long JSON lines, call scanner.Buffer or you lose everything after that line.带缓冲写
Go
f, _ := os.Create("out.txt")
defer f.Close()
w := bufio.NewWriter(f)
for i := 0; i < 3; i++ {
fmt.Fprintf(w, "第 %d 行\n", i)
}
w.Flush() // 必须!否则缓冲区内容丢失Output
$ cat out.txt
第 0 行
第 1 行
第 2 行bufio.Writer only flushes at 4 KB. Forgetting Flush() leaves an empty file while the program exits cleanly — invisible in unit tests.
目录操作
Go
// 创建(含父目录)
os.MkdirAll("logs/2026/10", 0755)
// 列目录
entries, err := os.ReadDir(".")
for _, e := range entries {
fmt.Println(e.IsDir(), e.Name())
}
// 递归遍历
filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() && strings.HasSuffix(path, ".go") {
fmt.Println(path)
}
return nil
})
// 文件信息
info, _ := os.Stat("demo.txt")
fmt.Println(info.Size(), info.ModTime().Format("2006-01-02"))Output
false demo.txt
true logs
main.go
2026-10-31 11:20常用组合
Go
// 复制文件
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in) // 自动分块,带缓冲
return err
}io.Copy loops with a 32 KB buffer and prefers WriterTo/ReaderFrom, so file-to-file copies go through sendfile. More robust than a hand-written loop.
一次性读写
Go
// 写
content := []byte("第一行\n第二行\n")
err := os.WriteFile("demo.txt", content, 0644)
if err != nil {
fmt.Println("写失败", err)
}
// 读
data, err := os.ReadFile("demo.txt")
if err != nil {
fmt.Println("读失败", err)
}
fmt.Print(string(data))
fmt.Println("字节数", len(data))Output
第一行
第二行
字节数 20"第一行\n" is 10 bytes (three CJK chars at 3 bytes plus newline), so two lines are 20. os.ReadFile loads the whole file into memory — small files only.
打开与关闭
Go
f, err := os.Open("demo.txt") // 只读
if err != nil {
return err
}
defer f.Close() // 必须
// 可写、追加、创建
f2, err := os.OpenFile("log.txt",
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return err
}
defer f2.Close()
f2.WriteString("新日志\n")| 标志 | 含义 |
|---|---|
os.O_RDONLY | 只读 |
os.O_WRONLY | 只写 |
os.O_RDWR | 读写 |
os.O_CREATE | 不存在就创建 |
os.O_TRUNC | 打开时清空 |
os.O_APPEND | 追加 |
逐行读大文件
Go
f, err := os.Open("big.log")
if err != nil {
return err
}
defer f.Close()
scanner := bufio.NewScanner(f)
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024) // 提高单行上限
lineNo := 0
for scanner.Scan() {
lineNo++
if strings.Contains(scanner.Text(), "ERROR") {
fmt.Println(lineNo, scanner.Text())
}
}
if err := scanner.Err(); err != nil {
return err
}Output
42 2026-10-31 ERROR 连接超时
87 2026-10-31 ERROR 写入失败bufio.Scanner 单行默认上限 64KB,超过会返回 token too long 并静默停止。日志里有超长 JSON 行时必须调 scanner.Buffer,否则会漏读后面的所有内容。带缓冲写
Go
f, _ := os.Create("out.txt")
defer f.Close()
w := bufio.NewWriter(f)
for i := 0; i < 3; i++ {
fmt.Fprintf(w, "第 %d 行\n", i)
}
w.Flush() // 必须!否则缓冲区内容丢失Output
$ cat out.txt
第 0 行
第 1 行
第 2 行bufio.Writer 攒够 4KB 才真正写盘。忘了 Flush() 时程序正常退出、文件却是空的——这种 bug 在只跑单元测试时看不出来。
目录操作
Go
// 创建(含父目录)
os.MkdirAll("logs/2026/10", 0755)
// 列目录
entries, err := os.ReadDir(".")
for _, e := range entries {
fmt.Println(e.IsDir(), e.Name())
}
// 递归遍历
filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() && strings.HasSuffix(path, ".go") {
fmt.Println(path)
}
return nil
})
// 文件信息
info, _ := os.Stat("demo.txt")
fmt.Println(info.Size(), info.ModTime().Format("2006-01-02"))Output
false demo.txt
true logs
main.go
2026-10-31 11:20常用组合
Go
// 复制文件
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in) // 自动分块,带缓冲
return err
}io.Copy 内部用 32KB 缓冲循环读写,比自己写 for { Read; Write } 稳。它还会优先用 WriterTo / ReaderFrom 优化,os.File 到 os.File 时直接走 sendfile 系统调用。