17370845950

Go 语言逐行读取文本文件并解析整数的正确方法

本文详解如何在 go 中安全、高效地逐行读取含整数的文本文件,并解决因

换行符残留导致 `strconv.atoi` 解析失败的常见问题。

在 Go 中逐行读取整数文本文件时,一个典型陷阱是:使用 bufio.Reader.ReadString('\n') 会将换行符 \n(或 Windows 下的 \r\n)包含在返回字符串中。正如问题中所观察到的——lineStr 长度为 4 而非 2,且内容实际为 "10\n"(UTF-8 编码下 '\n' 占 1 字节,'1' 和 '0' 各占 1 字节,共 3 字节;若输出显示长度为 4,很可能文件为 CRLF 换行,即 "10\r\n"),这导致 strconv.Atoi("10\n") 或 strconv.ParseUint("10\n", 0, 64) 无法识别为有效整数,返回 0 和错误。

虽然可通过 strings.TrimSpace(line) 手动清理空白符(包括 \n, \r, \t, 空格等),但更推荐、更符合 Go 习惯的方式是使用 bufio.Scanner:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

func main() {
    file, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        line := scanner.Text() // ✅ 自动去除行尾换行符,不包含 \n 或 \r\n
        num, err := strconv.Atoi(line)
        if err != nil {
            fmt.Printf("警告:跳过无效行 '%s' — %v\n", line, err)
            continue
        }
        fmt.Printf("解析成功:'%s' → %d\n", line, num)
    }

    if err := scanner.Err(); err != nil {
        panic(fmt.Sprintf("读取文件时出错:%v", err))
    }
}

关键优势

  • scanner.Text() 返回的字符串已自动剥离行终止符(\n、\r\n),无需额外 Trim;
  • 内部按行缓冲,内存友好,适合大文件;
  • 错误处理清晰:scanner.Err() 可捕获 I/O 错误,strconv.Atoi 的错误则用于校验数据格式。

⚠️ 注意事项

  • bufio.Scanner 默认单行最大长度为 64KB(bufio.MaxScanTokenSize),若需读取超长行,请提前设置:
    scanner := bufio.NewScanner(file)
    scanner.Buffer(make([]byte, 64*1024), 1<<20) // 最大支持 1MB 行
  • 始终检查 strconv.Atoi 的错误返回,避免静默失败(如空行、字母混入等);
  • 不要忽略 scanner.Err() —— 它是检测读取过程中底层 I/O 错误(如磁盘损坏、权限不足)的唯一途径。

总结:避免直接使用 ReadString('\n') 处理行数据;优先选用 bufio.Scanner,它专为“按行读取”设计,语义清晰、健壮性强、符合 Go 生态最佳实践。