bufio.Scanner 读不到换行符后的数据,因其默认以\n为分隔符且自动丢弃该符;末尾无换行时最后一次扫描可能被忽略;超长行触发“token too long”错误,需用Buffer()扩容;保留换行符应改用Reader的ReadBytes或ReadString。

bufio.Scanner 为什么读不到换行符后的数据?
因为 bufio.Scanner 默认以 \n 为分隔符,且会自动丢弃分隔符本身;若输入末尾无换行(比如用户 Ctrl+D 直接结束),最后一次扫描可能被忽略,或 Scan() 返回 false 却没报错。
- 用
scanner.Bytes()或scanner.Text()前,必须先调用scanner.Scan()并检查返回值 - 若需保留换行符,改用
bufio.Reader的ReadBytes('\n')或ReadString('\n') - 默认缓冲区大小为 64KB,超长行会触发
"bufio.Scanner: token too long"错误,可通过scanner.Buffer(nil, max)扩容
bufio.Reader.Read() 和 ReadSlice('\n') 行为差异
Read() 是底层字节读取,不识别分隔符,容易读多或读少;ReadSlice() 会一直读直到遇到指定字节(含该字节),但返回的是底层缓冲区的切片——如果后续继续调用其他读方法,该切片可能被覆盖。
- 安全做法:用
ReadString('\n')(返回新字符串)或对ReadSlice()结果立即做append([]byte{}, ...)拷贝 -
ReadSlice()在未找到分隔符时返回ErrBufferFull,不是 EOF —— 它只表示当前缓冲区满了,不代表输入结束 - 若输入源是管道或网络连接,
ReadSlice()可能阻塞等待分隔符;而Read()只要底层有数据就返回,更适合流式解析协议头
bufio.Writer.Flush() 什么时候必须显式调用?
只要写入的数据还没刷到底层 io.Writer(比如文件、网络连接),就存在丢失风险。常见于程序退出前、长连接中发送完整消息后、或写入量小于缓冲区容量时。
- 用
defer writer.Flush()不可靠——如果函数提前 return,defer仍会执行,但此时可能已关闭底层连接,导致 panic - 更稳妥的方式:
if err := writer.Flush(); err != nil { /* 处理错误 */ },尤其在循环写入后或 close 前 -
bufio.NewWriterSize(w, 0)创建无缓冲 writer,所有Write()都直通底层,无需 Flush,但性能差;生产环境慎用
bufio 包缓冲区大小设多少才合适?
没有通用最优值,取决于 I/O 类型和典型数据块大小。太小导致系统调用频繁;太大浪费内存且延迟高(尤其交互式输入)。
立即学习“go语言免费学习笔记(深入)”;
- 标准输入/输出:4KB–16KB 较平衡;
os.Stdin默认未包装,建议手动套bufio.NewReaderSize(os.Stdin, 8192) - 文件读写:可匹配文件系统块大小(如 4KB),或按单次处理单元调整(如 JSON 对象平均 2KB,则设 4KB)
- 网络连接:HTTP body 传输常用 32KB;但 WebSocket 消息较小,8KB 更合适;注意 TLS 层已有缓冲,再叠 bufio 可能冗余
缓冲区不是越大越好——它会锁住连续内存,并影响 GC 压力;超过 1MB 的缓冲区在高并发下容易引发内存抖动。










