Go语言如何按行读取文件_bufio Scanner读取文本文件

12次阅读

Scanner 读到“空行”就停是因为行长度超限而非真遇到空行;默认缓冲区64KB,超长行触发bufio.ErrTooLong导致Scan()返回false;应检查scanner.Err()并用scanner.Buffer()扩大缓冲区。

Go语言如何按行读取文件_bufio Scanner读取文本文件

为什么 Scanner 读到空行就停了?

默认情况下 bufio.Scanner 的缓冲区上限是 64KB,遇到超长行(比如日志中混入的 base64 字段、单行 jsON)会直接返回 scanner.Err() == bufio.ErrTooLong,且后续调用 scanner.Scan() 返回 false,看起来像“卡在空行”或“提前结束”。这不是空行问题,而是行长度越界导致扫描器终止。

  • scanner.Err() 检查是否为 bufio.ErrTooLong,而不是只看 Scan() 返回值
  • 可通过 scanner.Buffer(make([]byte, 64*1024), 1 手动扩大缓冲区上限(第二个参数是最大令牌长度,设为 1MB)
  • 若文件含真正不可控的超长行,建议改用 bufio.Reader.ReadLine() 或逐字节读取

Scanner 默认按什么分隔符切行?

bufio.Scanner 默认使用 bufio.ScanLines 作为分隔函数,它识别 nrnr 三种换行符,并**自动剥离**这些换行符。注意:r 单独出现时也会被当作行结束,这在处理旧 Mac 文件或某些串口输出时可能引发意外切分。

  • Windows 文件(rn)和 Unix 文件(n)都能正确处理
  • 若需保留换行符,不能用 scanner.Text(),应改用 scanner.Bytes() 并自行追加(但要注意 Bytes() 返回的是内部缓冲区切片,下一次 Scan() 后失效)
  • 自定义分隔符可用 scanner.Split(),例如按空行分割:传入 bufio.ScanLines 改写逻辑,或直接用 bytes.Split 预处理

如何安全地边读边解析 CSV 或 json 行?

每行一个 JSON 对象(JSON Lines)或 CSV 记录时,不能假设 scanner.Text() 返回的字符串一定合法——网络传输截断、编码错误、BOM 头都可能导致解析失败。必须对每一行单独做错误隔离。

  • 不要把所有行存进 slice 再批量解析;每调用一次 scanner.Scan() 就立即处理一行
  • json.Unmarshal([]byte(line), &v)if err != nil 判断,出错时打印 scanner.Bytes() 的原始字节(可 hex dump),方便定位乱码或截断位置
  • CSV 场景下,优先用 csv.NewReader(scanner) 替代手动 strings.Split(),它能正确处理带引号、换行、逗号的字段
file, _ := os.Open("data.jsonl") defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() {     line := scanner.Bytes() // 避免 string 转换丢失二进制数据     var record map[string]interface{}     if err := json.Unmarshal(line, &record); err != nil {         log.Printf("parse error at offset %d: %v, raw: %x", scanner.Bytes(), err)         continue     }     // 处理 record }

Scanner 和 ReadLine 哪个更适合大文件?

性能差异不大,但语义和错误处理完全不同:Scanner 是“按逻辑行”抽象,ReadLine() 是“按物理字节边界”读取。前者更易用,后者更可控。

立即学习go语言免费学习笔记(深入)”;

  • Scanner 自动跳过空白行(如果没禁用),而 ReadLine() 会把空行([]byte{})也返回
  • ReadLine() 遇到不完整行(末尾无换行符)时返回 isPrefix=true,需循环读取拼接;ScannerErrTooLong 时也要求你处理前缀,但逻辑更隐蔽
  • 若需精确控制内存(如流式解密后按行处理),ReadLine() 更合适,因为你能复用同一块 []byte 缓冲区

真正麻烦的是混合编码(UTF-8 + GBK)、行尾不统一、以及 Scanner 缓冲区复用机制带来的生命周期陷阱——scanner.Bytes() 不是新分配的内存,别把它塞进 goroutine 里异步用。

text=ZqhQzanResources