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
错误处理最易被忽略的点:**跨平台路径分隔符和权限语义差异**。Windows不区分0644和0755,而Linux下os.WriteFile("x.sh", []byte("#!/bin/sh"), 0644)写出来的脚本无法直接执行——得用os.Chmod补一次。