os.Open失败时file一定是nil且err非nil;必须先检查err再使用file;io.EOF不是错误,需显式判断;Close也可能失败,需单独记录;批量处理依场景选择跳过或中断。

os.Open 失败时,err 不为 nil,但 file 是 nil 吗?
不是。os.Open 在出错时返回的 *os.File 一定是 nil,而 err 非 nil —— 这是 Go 的约定。如果忽略 err 直接对 file 调用 Read 或 Close,会 panic:panic: runtime error: invalid memory address or nil pointer dereference。
- 必须先检查
err != nil,再使用file - 别写
f, _ := os.Open(...)—— 这是静默失败的温床 - 常见错误类型:路径不存在(
os.IsNotExist(err))、权限不足(os.IsPermission(err))、路径是目录却当文件打开(os.IsPermission也可能触发)
读取过程中遇到 io.EOF 算错误吗?
不算。它是正常流程终点,不是异常。把它当成错误处理(比如 log.Fatal 或 panic),会导致程序在读完文件时“意外崩溃”。
- 必须用
errors.Is(err, io.EOF)(Go 1.13+ 推荐)或err == io.EOF显式判断 -
bufio.Scanner.Scan()遇到 EOF 返回false,此时要调用scanner.Err()才知道是否真出错了 - 用
io.ReadAll或os.ReadFile时,io.EOF不会出现——它们只在整体失败时返回 error
file.Close() 也会失败,需要检查吗?
需要,尤其在写入后关闭时。比如磁盘已满、NFS 挂载断开、文件系统只读等场景,Close 可能返回 syscall.ENOSPC 或 syscall.EIO。
- 不要只写
defer file.Close()就以为万事大吉 - 生产代码建议用匿名函数包裹:
defer func() { if closeErr := file.Close(); closeErr != nil { log.Printf("关闭文件失败: %v", closeErr) } }() - 注意:关闭错误一般不覆盖主逻辑错误(如写入失败),而是单独记录;否则可能掩盖真正的问题源头
批量处理多个文件时,一个出错该中断还是跳过?
取决于上下文。配置加载类场景(如读取所有 .env.*)通常应跳过单个失败项并继续;而原子写入场景(如生成一组配套输出文件)则需全量回滚或提前退出。
立即学习“go语言免费学习笔记(深入)”;
- 遍历
os.ReadDir结果时,用continue跳过单个os.Open失败,避免整个流程卡死 - 别把
log.Printf当成处理——记录后仍要明确决定行为(跳过 / 终止 / 重试) - 临时性错误(如网络文件系统短暂不可达)可结合
context.WithTimeout和简单重试,但别无脑循环
实际开发中最容易被忽略的,不是“怎么写错误处理”,而是在每处 Read、Write、Close 后都机械地检查 err —— 它不优雅,但这就是 Go 的契约。










