生产环境推荐 bufio.Scanner 逐行读取——它自动处理 \r\n/\n 混合换行、支持流式读取大文件;需调用 scanner.Buffer 扩容防“token too long”,Scan() 后须检查 scanner.Err(),可用 Text() 或 Bytes() 获取内容。

用 bufio.Scanner 逐行读取最稳妥
直接用 os.ReadFile 读整个文件再 strings.Split 分行,看似简单,但遇到超大文件或含 \r\n/\n 混合换行时容易出错、内存暴涨。生产环境推荐 bufio.Scanner —— 它默认按行切分、自动处理不同换行符、支持大文件流式读取。
关键点:
-
Scanner默认每行上限 64KB,超长行会报scanner: token too long;需提前调用scanner.Buffer扩容 - 别忽略
scanner.Err():即使Scan()返回false,也可能是 I/O 错误而非文件结束 - 用
scanner.Text()获取字符串,scanner.Bytes()获取原始字节(适合含非法 UTF-8 的文件)
file, _ := os.Open("data.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
scanner.Buffer(make([]byte, 0, 64*1024), 1<<20) // 缓冲区扩容到 1MB
for scanner.Scan() {
line := scanner.Text() // 自动去换行符
// 处理 line...
}
if err := scanner.Err(); err != nil {
log.Fatal(err) // 必须检查
}
需要控制编码?先转 []byte 再解码
Go 标准库不内置字符编码转换,bufio.Scanner 和 os.ReadFile 都只认 UTF-8。如果 TXT 是 GBK、Shift-JIS 等编码,必须手动解码。
实操路径:
立即学习“go语言免费学习笔记(深入)”;
- 用
os.ReadFile或io.ReadAll读出原始[]byte - 用第三方库如
golang.org/x/text/encoding解码(例如encoding/gbk) - 解码失败时,
err != nil且返回部分有效字符串 + 错误位置,不能直接忽略
data, _ := os.ReadFile("gbk.txt")
decoder := gbk.NewDecoder()
text, err := decoder.String(string(data))
if err != nil {
// 可能是部分乱码,需记录 err.Offset 定位问题字节
}
ioutil.ReadFile 已弃用,改用 os.ReadFile
Go 1.16+ 中 ioutil.ReadFile 被标记为 deprecated,所有新代码必须用 os.ReadFile 替代。两者签名完全一致,但后者在内部做了小优化,且未来版本会彻底移除旧函数。
常见误操作:
- 仍从
"io/ioutil"导入,编译会警告;应改用"os" - 忘记检查返回的
error,尤其在文件不存在或权限不足时,data为 nil 但程序继续执行导致 panic - 对返回的
[]byte直接强制转string后再处理——若文件含 BOM(如 UTF-8-BOM),开头会多出\ufeff,影响后续字符串匹配
按字段解析(CSV 风格 TXT)别手写分割
如果 TXT 实际是制表符或逗号分隔的结构化数据(比如日志、导出表格),硬用 strings.Split(line, "\t") 极易出错:字段本身含制表符、空值缺失、引号包裹内容等场景都会崩。
正确做法:
- 用标准库
encoding/csv,它支持自定义分隔符、跳过 BOM、处理引号转义 - 设置
csv.NewReader(file).Comma = '\t'即可读 TSV - 注意
Read()返回的是[]string,不是string;空字段对应空字符串,不是nil
file, _ := os.Open("data.tsv")
reader := csv.NewReader(file)
reader.Comma = '\t'
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
// record[0], record[1]... 即各列
}
实际项目里,逐行读取只是起点。真正卡住人的往往是编码识别不准、BOM 处理遗漏、超长行边界没设、或者把半结构化文本当纯文本暴力切割——这些细节不提前想清楚,后面 debug 花的时间远超写代码。


















