bufio.readstring只识别'\n'(或'\r\n')为分隔符,传入其他rune会panic;它返回含换行符的string,遇eof且无'\n'时返回已读内容+io.eof,需正确处理该边界情况。

bufio.ReadString 读到换行符就停,不是按任意分隔符
bufio.ReadString 名字有误导性——它只认一个字符:'\n'(或 '\r\n' 在 Windows 文本中),不会识别你传进去的其他分隔符。比如传 ',' 或 '|',函数会直接 panic:「invalid argument: r: invalid rune」。这是因为它的参数类型是 rune,但底层逻辑只对 \n 做特殊处理,其他 rune 会被当作“非换行分隔符”并拒绝执行。
常见错误现象:
- 写
reader.ReadString(',')→ panic: invalid argument - 用
strings.Split处理整行后再取字段 → 忽略了流式读取的边界(比如字段里含换行)
想按逗号/竖线等分隔,该用 bufio.Scanner + SplitFunc
真正支持自定义分隔符的是 bufio.Scanner 的 Split 方法。你需要传一个 bufio.SplitFunc,它接收 []byte 和返回 (int, []byte, error),告诉 scanner “从哪截断、截出什么”。
实操建议:
- 对简单单字节分隔符(如
','),直接复用bufio.ScanLines+ 手动bytes.IndexByte更轻量 - 若需严格流式切分(例如日志中每条记录以
"---\n"分隔),才值得写自定义SplitFunc - 注意:scanner 默认缓冲区上限是 64KB,超长分隔段会报
bufio.Scanner: token too long,需调scanner.Buffer扩容
示例:按 | 切分(不跨行)
scanner := bufio.NewScanner(r)
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, '|'); i >= 0 {
return i + 1, data[0:i], nil
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
})
for scanner.Scan() {
fmt.Printf("field: %q\n", scanner.Text())
}
bufio.ReadString 和 bufio.ReadBytes 行为差异很关键
两者都停在 \n,但返回值不同:ReadString 返回 string 并包含 \n;ReadBytes 返回 []byte 同样包含 \n。这意味着如果你后续要 strings.TrimSpace,用 ReadString 更方便;但若要做二进制处理或避免字符串拷贝,ReadBytes 更合适。
性能与兼容性影响:
-
ReadString内部会做bytes.ToString,有额外分配;高吞吐场景下ReadBytes+bytes.TrimSuffix更省 - Windows 文件用
\r\n换行时,ReadString会把\r\n全包进结果,得手动strings.TrimSuffix(s, "\r\n")或strings.TrimRight(s, "\r\n") - 如果输入不含
\n(比如最后一行没换行符),ReadString会一直阻塞直到 EOF 或超时
别忽略 io.EOF 和部分读取的边界情况
bufio.ReadString 在遇到 EOF 且缓冲区中没有 \n 时,会返回已读内容 + io.EOF。这不是错误,而是正常流结束信号。很多人误判为异常而中断处理,导致丢掉最后一段数据。
正确做法:
- 检查 err 是否等于
io.EOF,是则把str当有效数据处理 - 检查 err 是否为
nil,才是完整一行;其他 err(如网络断连)才该退出 - 如果读到空字符串 +
io.EOF,说明文件以\n结尾,这是合法的
容易被忽略的点:当输入来自管道或网络连接时,io.EOF 可能随时出现,且和“数据读完”不是一回事——它只代表读端关闭,不代表所有业务数据已送达。这时候需要结合协议层校验(比如长度前缀或结束标记)才能确认完整性。











