Golang文件I/O中的常见错误处理方式

12次阅读

go文件操作需严格检查Error、及时close、分块读写防OOM、Write后校验n值、关键场景调用Sync。忽略任一环节均可能导致panic、fd泄漏、内存溢出或数据丢失

Golang文件I/O中的常见错误处理方式

open() 返回 *os.File 但忽略 err 判断

Go 的 os.Openos.Createos.OpenFile 都返回 (*os.File, error),常见错误是只取第一个值却没检查 err 是否为 nil。一旦路径不存在、权限不足或磁盘满,*os.Filenil,后续调用 Read()Write() 会 panic。

正确做法始终先判断 err

file, err := os.Open("config.txt") if err != nil {     log.Fatal("failed to open file:", err) // 或 return err } defer file.Close()
  • 不要用 if file == nil 替代 err != nil —— os.Open 在出错时可能返回非 nil*os.File(如部分系统调用成功但元数据读取失败)
  • 使用 errors.Is(err, os.ErrNotExist) 区分具体错误类型,便于差异化处理(比如缺配置文件时加载默认值)

忘记 close() 或 defer 放错位置

os.File 是对系统文件描述符的封装,不显式 Close() 会导致 fd 泄漏,进程最终因“too many open files”崩溃。常见陷阱包括:

  • defer file.Close() 写在 err != nil 分支之后 —— 错误时 filenil,调用 Close() panic
  • 循环中反复 os.Open 却只在循环外 defer —— 实际只关闭最后一次打开的文件
  • bufio.NewReader(file) 后,误以为 reader 负责关闭底层 file —— 它不会

安全写法:在确认 file != nilerr == nil 后立即 defer

立即学习go语言免费学习笔记(深入)”;

f, err := os.Open(path) if err != nil {     return err } defer f.Close() // 此时 f 必然非 nil

ReadAll() 在大文件上触发 OOM

ioutil.ReadAll()(Go 1.16+ 推荐用 io.ReadAll())会把整个文件读进内存。对几百 MB 以上的文件,极易导致程序被系统 kill。

  • 替代方案:用 os.Open + bufio.Scanner 按行处理,或 io.CopyN()/io.ReadFull() 分块读取
  • 若必须全读,先用 os.Stat().Size() 检查大小,超阈值(如 10MB)则拒绝或走流式路径
  • io.ReadAll() 返回的 []byte 和原 file 无绑定关系 —— 关闭文件不影响已读数据,但别指望它自动释放底层资源

WriteString() / Write() 不检查返回的 n 和 err

Write() 系列方法返回 (int, error),其中 int 是实际写入字节数。常见错误是只判 err,忽略 n 是否等于预期长度。

  • 网络文件系统(如 NFS)、满磁盘、信号中断等场景下,Write() 可能只写入部分数据并返回 nil 错误 —— 这是合法行为,需循环重试或用 io.WriteString()(它内部已做补全)
  • fmt.Fprint() 等包装函数也返回 (int, error),同样不能只看 err
  • 写日志或关键配置时,建议用 file.Sync() 强制刷盘,否则程序崩溃可能导致最后几 KB 数据丢失

文件 I/O 的错误不是“有没有”,而是“在哪一层漏掉”。最常被跳过的其实是 Close() 的返回值 —— 它也可能报错(比如写缓冲区 flush 失败),但很少有人检查。

text=ZqhQzanResources