Go中os.Open和os.Create等I/O函数必须检查返回的err,因Go无异常机制;忽略错误会导致静默失败或panic;需用if err != nil判断,并用defer f.Close()前确保f非nil。

Go中os.Open和os.Create返回错误必须检查
Go不支持异常机制,所有I/O操作的错误都通过返回值显式传递。忽略err会导致程序在文件不存在、权限不足或磁盘满时静默失败,甚至panic(比如对nil *os.File调用Write)。
常见错误现象:open /tmp/data.txt: no such file or directory、permission denied、too many open files。
- 永远用
if err != nil判断os.Open、os.Create、os.Stat等函数的返回值 - 不要只打印
err.Error(),优先用fmt.Printf("failed to open %s: %v", path, err)带上上下文 - 对临时文件或日志路径,建议先用
os.MkdirAll(filepath.Dir(path), 0755)确保父目录存在
用defer f.Close()前必须确认f非nil
defer不能挽救打开失败的情况。如果os.Open返回nil, err,后续defer f.Close()会触发panic:“invalid memory address or nil pointer dereference”。
file, err := os.Open("config.json")
if err != nil {
log.Fatal(err) // 或 return err
}
defer file.Close() // 此时 file 一定非nil
- 把
defer f.Close()写在if err != nil检查之后,且在同一作用域内 - 不要在
if err != nil分支里写defer——它不会执行 - 若需统一关闭多个资源(如读+写文件),用
if f != nil { f.Close() }手动清理
ioutil.ReadFile和os.WriteFile简化错误处理但有局限
Go 1.16+推荐用os.ReadFile和os.WriteFile替代旧的ioutil(已弃用)。它们封装了打开、读/写、关闭全过程,错误只来自单次调用,逻辑更扁平。
data, err := os.ReadFile("input.txt")
if err != nil {
// 处理读取失败:文件不存在、权限、损坏等
}
err = os.WriteFile("output.txt", data, 0644)
if err != nil {
// 处理写入失败:磁盘满、只读文件系统、父目录不可写等
}
-
os.ReadFile把整个文件加载进内存,不适合GB级大文件 -
os.WriteFile会覆盖原文件;如需追加,仍得用os.OpenFile配os.O_APPEND - 权限参数
0644在Windows上被忽略,实际由系统ACL决定
区分错误类型:用os.IsNotExist和os.IsPermission
不是所有错误都该同等处理。例如,文件不存在可自动创建,而权限拒绝则需提示用户改路径或提权。
_, err := os.Stat("cache.db")
if err != nil {
if os.IsNotExist(err) {
log.Println("cache missing, initializing...")
createCache()
} else if os.IsPermission(err) {
log.Fatal("no permission to access cache.db — check file ownership")
} else {
log.Fatal("unexpected stat error:", err)
}
}
- 避免用
strings.Contains(err.Error(), "no such file")做判断——平台相关且脆弱 -
os.IsNotExist兼容Linux/Windows/macOS的不同底层错误码 - 其他常用判断函数:
os.IsExist、os.IsTimeout、os.IsInterrupted
0644和0755,而Linux下os.WriteFile("x.sh", []byte("#!/bin/sh"), 0644)写出来的脚本无法直接执行——得用os.Chmod补一次。










