必须用os.openfile配合os.o_append、os.o_wronly、os.o_create三标志,缺一不可;writestring不自动换行,需手动添加;并发写需加锁,gob追加须加长度前缀和魔数校验。

必须用 os.OpenFile 配合三个 flag: os.O_APPEND、os.O_WRONLY、os.O_CREATE
只传 os.O_APPEND 不行,缺了 os.O_WRONLY 会报 “invalid argument”;不带 os.O_CREATE 且文件不存在时直接返回 no such file or directory;漏掉 os.O_APPEND 则无论 Seek 到哪,写入都会从头覆盖。
常见错误写法:os.Create("x.log") 等价于带 os.O_TRUNC,每次重启日志就清空;os.OpenFile("x.log", os.O_WRONLY, 0644) 没有 os.O_APPEND,首次写就截断全文件。
-
os.O_APPEND是内核级原子保障:每次Write前自动定位到当前末尾,不依赖用户Seek -
os.O_WRONLY避免读开销,纯写场景比os.O_RDWR更轻量 -
os.O_CREATE只在文件不存在时生效,不影响已有文件权限,但父目录必须可写
WriteString 不自动换行,内容拼接要自己控制
Go 的 WriteString 就是裸字节写入,不会补 \n 或 \r\n。连续两次 f.WriteString("a"); f.WriteString("b") 结果是 ab,不是两行。
- 文本日志建议统一加
"\n";若需 Windows 记事本正常显示,用"\r\n" - 内容本身含换行(如用户输入的多行字符串),直接写即可,额外加换行会导致空行
- 别依赖
fmt.Fprintln——它只是WriteString+\n,多一层格式化无实质优势
并发写同一文件末尾,os.O_APPEND 不等于线程安全
os.O_APPEND 保证每次系统调用写入位置正确,但 Go 层的 WriteString 不是原子操作:它先拷贝进内部 buffer,再 syscall。多个 goroutine 同时调用,buffer 写入可能交错,出现半行乱码或两段文字粘连。
- 简单场景:用
sync.Mutex包住单次WriteString或Write调用 - 高频日志:直接用标准库
log.New(f, "", log.LstdFlags),内部已加锁并自带时间戳和换行 - 避免用 channel 聚合再写——缓冲区满或 goroutine 阻塞会导致日志延迟甚至丢失
大文件或 gob 追加要额外帧封装,不能直接复用 encoder
encoding/gob 本身不支持追加:每次 gob.Encoder.Encode 都重写类型描述头,多次写入会产生多个无法连贯解析的流,解码时必然 panic extra data in buffer。
- 正确做法:每次 encode 前先写入长度前缀(如
uint32小端),再写 payload - 读取时循环:先读 4 字节得长度
n,再读n字节进bytes.Reader,再用新gob.NewDecoder解码 - 避免动态
gob.Register:进程重启后类型 ID 映射不一致,帧内类型头可能失效;应提前注册所有可能类型
真正容易被忽略的是魔数校验和缓冲区 flush —— 二进制追加前不检查魔数,损坏文件头会导致后续全部解析失败;用 bufio.Writer 加速时,Close() 不触发 Flush(),panic 或 crash 会让最后一段缓冲数据彻底消失。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











