Go文件操作需注意五点:open/close必须成对;读大文件须流式处理;写文件慎用os.WriteFile;路径用filepath.Join、编码需处理BOM;多goroutine写需加锁。
Go 中用 os.Open 或 os.OpenFile 打开文件后,必须显式调用 Close(),Go 不会自动回收。漏掉 Close() 在长期运行服务中会导致 too many open files 错误。
defer f.Close(),但注意它在函数返回时才执行,若函数体很长或有多个 return,仍可能因 panic 未执行到 defer —— 更稳妥的是用 if err != nil { f.Close(); return err } 配合提前返回os.Open 只支持只读;写入或追加必须用 os.OpenFile 并传入正确 flag,例如 os.O_WRONLY|os.O_CREATE|os.O_TRUNC
*os.File 本身不是并发安全的ioutil.ReadAll(Go 1.16+ 已移至 io.ReadAll)会把整个文件一次性加载进内存,几 GB 的日志文件极易触发 OOM。真实场景应按需流式处理。
bufio.Scanner,默认单行上限 64KB,超长行会报 scanner: token too long,可调 ScanBytes 或 Bufio.NewReader + ReadString('\n')
io.Copy(如复制文件)或手动 buf := make([]byte, 32*1024); n, _ := f.Read(buf)
json.NewDecoder(f) 或 csv.NewReader(f),它们内部已做缓冲和流式解析os.WriteFile 看似简单:传路径、字节切片、权限即可。但它底层是先写临时文件再 rename,且**不支持追加**,每次调用都会覆盖全量内容。
os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) + f.Write
os.WriteFile 的权限参数在 Windows 下被忽略,实际由系统 ACL 控制;Linux/macOS 上若传 0600 却期望组用户可读,会出意料之外的权限问题os.WriteFile 的原子性依赖 rename,而某些网络文件系统(如 NFSv3)不保证 rename 原子,此时应手写“写临时文件 → fsync → rename”流程Go 的 os 包操作路径时,"a/b.txt" 在 Windows 上会被自动转为 a\b.txt,但如果你拼接字符串用了 "a\\b.txt" 再传给 os.Open
,在 Linux 下就会打不开。BOM 同理:UTF-8 文件带 BOM 时,io.ReadAll 返回的字节开头是 0xEF 0xBB 0xBF,不手动剔除会导致 JSON 解析失败等静默错误。
立即学习“go语言免费学习笔记(深入)”;
filepath.Join("a", "b", "c.txt"),不要用字符串 + 或 fmt.Sprintf
data, _ := os.ReadFile(path)
if len(data) >= 3 && bytes.Equal(data[:3], []byte{0xEF, 0xBB, 0xBF}) {
data = data[3:]
}"\n"(Unix 风格),Windows 程序能正确识别;避免硬写 "\r\n",除非明确要求兼容老旧工具