Golang读取文本文件按行处理示例

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...) 复制一份,否则下一次调用会覆盖。

  • 返回的 isPrefixtrue 表示行太长被截断,需循环读取直到 isPrefix == false
  • 遇到空行时返回空切片 []byte{},不是 nil,别用 == nil 判空
  • Scanner 多一层控制,但也多一层出错可能 —— 比如忘记处理 isPrefix

按行读取时忽略 BOM 是个高频坑

Windows 上用记事本保存的 UTF-8 文件常带 BOM(0xEF 0xBB 0xBF),bufio.Scanner 不会自动跳过它,第一行开头会出现乱码字符。这不是编码错误,而是字节被当作文本内容读进来了。

  • 打开文件后,先用 bytes.HasPrefix 检查前三个字节是否为 BOM,是则用 io.MultiReader 跳过
  • 更简单的方法:用 golang.org/x/text/encoding/unicode 包的 UTF8.NewDecoder().Bytes() 预处理整行(但会失去流式优势)
  • 如果确定文件来源固定,可在写入端统一禁用 BOM(比如 VS Code 设置 "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),它们内部已处理好边界问题
BOM 和跨块换行符对齐这两点,初学者常以为“读到换行就完事”,结果线上跑几天才发现第一行或某几行内容异常 —— 它们不会报错,只会静默出错。