小文件用os.readfile/writefile,大文件或需流控时用os.open+io.copy或bufio.scanner;ioutil已自go 1.16起彻底移除。

小文件直接用 os.ReadFile 和 os.WriteFile,大文件或需控制流程时必须用 os.Open + io.Copy 或 bufio.Scanner;ioutil 已在 Go 1.16+ 彻底移除,继续用会编译失败。
什么时候该用 os.ReadFile 而不是手动 os.Open/os.Close
90% 的配置读取、单次 JSON/YAML 解析、临时数据存取都适用。它自动完成打开 → 读取 → 关闭全流程,不漏关句柄,也不需要写 defer f.Close()。
- 错误必须检查:
os.ReadFile返回"no such file or directory"或"permission denied"时不处理,后续操作大概率 panic -
os.ReadFile返回的是[]byte,要当字符串用得显式转:string(data),它不校验 UTF-8 合法性 - 写入权限(如
0644)只在创建新文件时生效,Windows 上基本忽略;设成0会导致 Linux/macOS 下文件不可读 -
os.WriteFile总是覆盖写,要追加?换os.OpenFile+os.O_APPEND
为什么几十 MB 就不该用 os.ReadFile
它把整个文件一次性加载进内存,没流控、无缓冲区调节能力。几十 MB 就可能触发 runtime: out of memory,尤其在低内存容器或嵌入式环境里更敏感。
- 日志分析、GB 级备份、边读边解密/压缩等场景,必须流式处理
-
bufio.Scanner默认单行上限 64KB,超长行会报scanner.ErrTooLong;调大上限前先确认是否真需要——很多“超长行”其实是日志格式错误 -
io.Copy内部用 32KB 缓冲区,适合整块搬运(比如复制视频、保存 HTTP 响应体),不用手写循环读写逻辑
os.OpenFile 的 flag 组合决定实际行为
os.OpenFile 是底层统一入口,os.Open、os.Create 都是它的封装。真正控制“能不能写”“找不到是否创建”“是否清空”“是否追加”的,就是 flag 参数。
- 只读:
os.O_RDONLY - 读写 + 创建(不存在时):
os.O_RDWR | os.O_CREATE - 追加写(常用日志):
os.O_WRONLY | os.O_APPEND | os.O_CREATE - 覆盖写(清空后重写):
os.O_WRONLY | os.O_CREATE | os.O_TRUNC - 权限位(如
0644)仅在创建新文件时起作用,已存在文件的权限不会被修改
忘记 defer f.Close() 是高频事故
Linux 下默认最多 1024 个打开文件,跑久直接卡死在 too many open files。哪怕用了 defer,也要注意它在函数 return 后才执行——中间 panic 且没 recover,仍可能漏关。
- 别在循环里反复调用
os.Open+os.WriteFile处理多行:每轮都开闭文件,I/O 开销爆炸 - 用
bufio.Scanner逐行读时,scanner.Text()返回拷贝,scanner.Bytes()返回切片引用——后者更快但生命周期受 scanner 控制,别跨循环保存 -
io.Copy不关心复制了多少字节,只返回 error;如需精确计数或进度反馈,得用io.CopyN或自定义io.Writer
最易被忽略的点是:权限参数和 flag 行为只在创建/打开瞬间生效,后续对已存在文件的读写操作完全不受影响;还有,os.WriteFile 的覆盖语义是原子的,但不提供文件锁,多进程并发写同一路径时仍需外部协调。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











