小文件用 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 而不是自己 open/close
90% 的配置读取、临时数据写入、单次 JSON/YAML 解析都适用。它自动处理打开、读/写、关闭,不漏关句柄,也不需要 defer。
- 错误必须检查:返回
"no such file or directory"或"permission denied"时不处理,程序就 panic -
os.ReadFile返回[]byte,要转字符串得显式写string(data),它不校验 UTF-8 - 写入权限参数(如
0644)在 Linux/macOS 上生效,Windows 忽略;设成0会导致文件不可读 - 它总是覆盖写,要追加?换
os.OpenFile+os.O_APPEND
大文件为什么不能用 os.ReadFile
几十 MB 就可能触发 runtime: out of memory —— 它把整个文件一次性加载进内存,没流控、无缓冲区调节能力。
- 日志分析、GB 级备份、边读边解密等场景,必须流式处理
-
bufio.Scanner默认单行上限 64KB,超长行会报scanner.ErrTooLong;可调但别乱改分割逻辑 -
io.Copy内部用 32KB 缓冲区,适合整块搬运(比如复制视频、下载保存),不用手写循环 - 忘记
defer f.Close()是高频事故,Linux 下默认最多 1024 个打开文件,跑久直接卡死在too many open files
os.OpenFile 的 flag 组合决定实际行为
os.OpenFile 是底层统一入口,os.Create、os.Open 都是它的封装。真正控制“能不能写”“找不到是否创建”“是否清空”“是否追加”的,就是 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)仅在创建新文件时起作用,已存在文件的权限不会被修改
逐行读取时 scanner.Text() 和 scanner.Bytes() 的区别
这是容易引发内存误用的细节:两者的底层数据来源相同,但语义和生命周期完全不同。
-
scanner.Text()返回string,是拷贝出来的独立副本,安全但有分配开销 -
scanner.Bytes()返回[]byte,是底层缓冲区的切片引用,速度快,但下一次Scan()后内容会被覆盖 - 如果要把某行存起来后续用,必须拷贝:比如
append([]byte{}, line...)或string(line) - 用
bufio.NewReader+ReadString('\n')也能逐行,但不自动处理长行截断,不如Scanner安全
最常被忽略的是缓冲区生命周期和错误传播路径——scanner.Err() 必须在循环结束后显式检查,否则 I/O 错误(比如磁盘满、网络中断)会静默丢失;而 io.Copy 的 error 是唯一出口,别只看返回字节数。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











