1 文件读写
Go 提供两种文件读写方式:os.ReadFile / os.WriteFile 适合小文件一次性读写;os.Open / os.Create 适合大文件或需要流式处理的场景。
简单方式 — 一次性读写
package main
import (
"fmt"
"os"
)
func main() {
// 写入文件(文件不存在则创建,存在则覆盖)
content := []byte("Hello, Go!\n这是文件内容。\n")
err := os.WriteFile("output.txt", content, 0644)
if err != nil {
fmt.Println("写入失败:", err)
return
}
// 读取整个文件到内存
data, err := os.ReadFile("output.txt")
if err != nil {
fmt.Println("读取失败:", err)
return
}
fmt.Println(string(data))
}
流式方式 — os.Open / os.Create
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 创建/覆盖文件
file, err := os.Create("stream.txt")
if err != nil {
panic(err)
}
defer file.Close()
file.WriteString("第一行\n")
file.WriteString("第二行\n")
file.Write([]byte("第三行\n"))
// 打开文件读取
f, err := os.Open("stream.txt")
if err != nil {
panic(err)
}
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
panic(err)
}
fmt.Println(string(data))
// 追加模式打开文件
af, err := os.OpenFile("stream.txt", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer af.Close()
af.WriteString("追加的内容\n")
}
文件信息与判断
// 检查文件是否存在
func fileExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
// 获取文件信息
info, err := os.Stat("output.txt")
if err == nil {
fmt.Println("文件名:", info.Name())
fmt.Println("大小:", info.Size(), "字节")
fmt.Println("修改时间:", info.ModTime())
fmt.Println("是否目录:", info.IsDir())
fmt.Println("权限:", info.Mode())
}
始终 defer file.Close():打开文件后立即用 defer 关闭,确保函数退出时释放文件句柄,避免资源泄漏。
2 bufio 读写
bufio 包提供带缓冲的读写操作,大幅提升 IO 性能,特别适合按行处理大文件。
按行读取 — bufio.Scanner
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("data.txt")
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
lineNum := 0
for scanner.Scan() {
lineNum++
fmt.Printf("%d: %s\n", lineNum, scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("读取错误:", err)
}
}
bufio.Reader — 灵活读取
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("data.txt")
if err != nil {
panic(err)
}
defer file.Close()
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
if len(line) > 0 {
fmt.Print(line)
}
break
}
panic(err)
}
fmt.Print(line)
}
}
bufio.Writer — 缓冲写入
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Create("buffered.txt")
if err != nil {
panic(err)
}
defer file.Close()
writer := bufio.NewWriter(file)
for i := 1; i <= 1000; i++ {
fmt.Fprintf(writer, "第 %d 行数据\n", i)
}
// 必须调用 Flush 将缓冲区数据写入文件
writer.Flush()
}
Scanner 默认缓冲区:默认最大 token 大小为 64KB。处理超长行时需要用 scanner.Buffer(buf, maxSize) 扩大缓冲区。
3 目录操作
创建目录
package main
import (
"fmt"
"os"
)
func main() {
// 创建单层目录
err := os.Mkdir("mydir", 0755)
if err != nil {
fmt.Println(err)
}
// 递归创建多层目录(类似 mkdir -p)
err = os.MkdirAll("path/to/nested/dir", 0755)
if err != nil {
fmt.Println(err)
}
// 删除空目录
os.Remove("mydir")
// 递归删除目录及其内容
os.RemoveAll("path")
}
读取目录内容 — os.ReadDir
package main
import (
"fmt"
"os"
)
func main() {
entries, err := os.ReadDir(".")
if err != nil {
panic(err)
}
for _, entry := range entries {
info, _ := entry.Info()
if entry.IsDir() {
fmt.Printf("[目录] %-20s\n", entry.Name())
} else {
fmt.Printf("[文件] %-20s %d 字节\n", entry.Name(), info.Size())
}
}
}
递归遍历 — filepath.WalkDir
package main
import (
"fmt"
"io/fs"
"path/filepath"
)
func main() {
root := "."
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
// 跳过隐藏目录
if d.IsDir() && path != root && path[0] == '.' {
return filepath.SkipDir
}
indent := ""
depth := len(filepath.SplitList(path))
for i := 0; i < depth; i++ {
indent += " "
}
if d.IsDir() {
fmt.Printf("%s📁 %s/\n", indent, d.Name())
} else {
fmt.Printf("%s📄 %s\n", indent, d.Name())
}
return nil
})
if err != nil {
fmt.Println("遍历失败:", err)
}
}
路径处理 — path/filepath
package main
import (
"fmt"
"path/filepath"
)
func main() {
// 拼接路径(自动处理分隔符)
p := filepath.Join("home", "user", "documents", "file.txt")
fmt.Println(p) // home/user/documents/file.txt
// 获取目录和文件名
fmt.Println(filepath.Dir(p)) // home/user/documents
fmt.Println(filepath.Base(p)) // file.txt
fmt.Println(filepath.Ext(p)) // .txt
// 绝对路径
abs, _ := filepath.Abs(".")
fmt.Println("当前绝对路径:", abs)
// 通配符匹配
matches, _ := filepath.Glob("*.go")
fmt.Println("Go 文件:", matches)
// 相对路径
rel, _ := filepath.Rel("/home/user", "/home/user/docs/file.txt")
fmt.Println(rel) // docs/file.txt
}
4 JSON 文件处理
读写 JSON 文件
package main
import (
"encoding/json"
"fmt"
"os"
)
type Config struct {
Host string `json:"host"`
Port int `json:"port"`
Debug bool `json:"debug"`
AllowIPs []string `json:"allow_ips"`
}
func saveConfig(path string, cfg *Config) error {
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
}
return os.WriteFile(path, data, 0644)
}
func loadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, err
}
return &cfg, nil
}
func main() {
cfg := &Config{
Host: "localhost",
Port: 8080,
Debug: true,
AllowIPs: []string{"127.0.0.1", "192.168.1.0/24"},
}
saveConfig("config.json", cfg)
fmt.Println("配置已保存")
loaded, err := loadConfig("config.json")
if err != nil {
panic(err)
}
fmt.Printf("加载配置: %s:%d, debug=%v\n", loaded.Host, loaded.Port, loaded.Debug)
}
流式处理大 JSON 文件
package main
import (
"encoding/json"
"fmt"
"os"
)
type Record struct {
ID int `json:"id"`
Name string `json:"name"`
}
func writeRecords(path string, records []Record) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
return encoder.Encode(records)
}
func readRecords(path string) ([]Record, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
var records []Record
if err := json.NewDecoder(file).Decode(&records); err != nil {
return nil, err
}
return records, nil
}
func main() {
records := []Record{
{ID: 1, Name: "张三"},
{ID: 2, Name: "李四"},
{ID: 3, Name: "王五"},
}
writeRecords("records.json", records)
loaded, _ := readRecords("records.json")
for _, r := range loaded {
fmt.Printf("ID: %d, Name: %s\n", r.ID, r.Name)
}
}
5 CSV 处理
encoding/csv 包提供 CSV 文件的读写支持,自动处理引号、转义等细节。
写入 CSV
package main
import (
"encoding/csv"
"os"
)
func main() {
file, err := os.Create("users.csv")
if err != nil {
panic(err)
}
defer file.Close()
// 写入 UTF-8 BOM(Excel 兼容)
file.Write([]byte{0xEF, 0xBB, 0xBF})
writer := csv.NewWriter(file)
defer writer.Flush()
// 写入表头
writer.Write([]string{"ID", "姓名", "邮箱", "年龄"})
// 写入数据行
data := [][]string{
{"1", "张三", "zhangsan@example.com", "28"},
{"2", "李四", "lisi@example.com", "32"},
{"3", "王五", "wangwu@example.com", "25"},
}
for _, row := range data {
writer.Write(row)
}
}
读取 CSV
package main
import (
"encoding/csv"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("users.csv")
if err != nil {
panic(err)
}
defer file.Close()
reader := csv.NewReader(file)
// 读取全部数据
// records, err := reader.ReadAll()
// 逐行读取(适合大文件)
header, err := reader.Read()
if err != nil {
panic(err)
}
fmt.Println("表头:", header)
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
fmt.Println("读取错误:", err)
continue
}
fmt.Printf("ID=%s, 姓名=%s, 邮箱=%s\n", record[0], record[1], record[2])
}
}
自定义分隔符
// 使用 Tab 分隔的 TSV 文件
reader := csv.NewReader(file)
reader.Comma = '\t'
reader.Comment = '#' // 以 # 开头的行视为注释
reader.TrimLeadingSpace = true
writer := csv.NewWriter(file)
writer.Comma = '\t'
CSV 编码注意:Go 默认使用 UTF-8,生成供 Excel 打开的 CSV 时建议在文件开头写入 BOM(0xEF, 0xBB, 0xBF),否则中文可能乱码。
6 本章要点
📄 文件读写
- •
os.ReadFile/os.WriteFile一次性 - •
os.Open/os.Create流式 - •
os.OpenFile追加模式
📖 bufio
- •
bufio.Scanner按行读取 - •
bufio.NewReader灵活读取 - •
bufio.NewWriter+Flush()
📁 目录操作
- •
os.MkdirAll递归创建 - •
os.ReadDir列出内容 - •
filepath.WalkDir递归遍历
🗂️ 路径处理
- •
filepath.Join拼接路径 - •
filepath.Dir/Base/Ext - •
filepath.Glob通配符匹配
📋 JSON 文件
- •
json.MarshalIndent美化写入 - •
json.NewEncoder/NewDecoder - • 配置文件读写场景
📊 CSV 处理
- •
csv.NewReader/csv.NewWriter - • 自定义分隔符和注释符
- • UTF-8 BOM 兼容 Excel