bufio.Scanner 是 Go 按行读取文本的首选,自动处理换行符、内存可控、默认单行上限 64KB;避免 os.ReadFile+strings.Split(易 OOM)和 bufio.Reader.ReadString(易漏末行);需检查 scanner.Err() 而非仅 io.EOF;超长行可调用 scanner.Buffer 自定义缓冲区。
bufio.Scanner 按行读取最稳妥绝大多数场景下,bufio.Scanner 是 Go 里按行处理文本文件的首选。它自动处理换行符(\n、\r\n),内存占用可控,且默认单行上限 64KB —— 对普通日志或配置文件完全够用。
常见错误是直接用 os.ReadFile + strings.Split 一次性加载整个文件,大文件容易 OOM;或者用 bufio.Reader.ReadString('\n') 手动处理,结果漏掉最后一行(没换行符时返回 io.EOF 但不报错)。
scanner.Scan() 循环,每次调用后用 scanner.Text() 获取当前行(不含换行符)scanner.Err() 判断是否因 I/O 错误提前退出,别只看 io.EOF
scanner.Buffer(make([]byte, 0), 1 扩容缓冲区
file, err := os.Open("data.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
// 处理每一行,例如:strings.TrimSpace(line)
fmt.Println(line)
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
bufio.Reader.ReadLine() 适合需要原始字节或控制换行符的场景bufio.Reader.ReadLine() 返回 []byte 和一个布尔值,表示该行是否被截断(超过缓冲区长度)。它不自动去掉换行符,也不合并 \r\n,适合你明确要保留原始换行格式、或后续做二进制解析的情况。
注意:它返回的是切片,指向底层缓冲区,如果要长期保存某一行内容,必须用 append([]byte{}, line...) 复制一份,否则下一次调用会覆盖。
isPrefix 为 true 表示行太长被截断,需循环读取直到 isPrefix == false
[]byte{},不是 nil,别用 == nil 判空Scanner 多一层控制,但也多一层出错可能 —— 比如忘记处理 isPrefix
Windows 上用记事本保存的 UTF-8 文件常带 BOM(0xEF 0xBB 0xBF),bufio.Scanner 不会自动跳过它,第一行开头会出现乱码字符。这不是编码错误,而是字节被当作文本内容读进来了。
bytes.HasPrefix 检查前三个字节是否为 BOM,是则用 io.MultiReader 跳过golang.org/x/text/encoding/unicode 包的 UTF8.NewDecoder().Bytes() 预处理整行(但会失去流式优势)"files.encoding": "utf8" 并关掉 "files.autoGuessEncoding")当文件
上百 MB 以上,且每行极短(如 CSV 记录),Scanner 仍够用;但如果单行本身长达几 MB,或你需要严格控制每次系统调用大小(比如限流读取网络响应体),就得绕过 Scanner,用 bufio.Reader.Read() 分块读,再自己找换行符位置。
这时关键难点是:换行符可能被切在两块之间。例如块末尾是 "hello\r",下一块开头是 "\nworld"。必须把上一块末尾的 \r 和下一块开头的 \n 拼起来识别为完整行尾。
\r 结尾时),下一轮读取后拼接再判断bytes.IndexByte(buf, '\n') 简单切分 —— 它无法处理 \r\n 组合github.com/apache/arrow/go/arrow/ipc(针对结构化数据)或 encoding/csv(针对 CSV),它们内部已处理好边界问题