Go 文件处理

读写文件、bufio、目录遍历、以及大文件的分块处理。

Intermediate
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")
FlagMeaning
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.

Friends & Partners