bufio.Scanner是Go中逐行读取大文件最稳妥方案,内存可控且自动处理换行符;需检查scanner.Err()而非仅Scan()返回值,超长行须调用scanner.Buffer()扩容,保留行内容需深拷贝避免缓冲区覆盖。

用 bufio.Scanner 逐行读取并匹配最稳妥
直接用 os.ReadFile 读整个文件再 strings.Split 切割,在大文件上容易 OOM;bufio.Scanner 按行流式处理,内存可控,且默认缓冲区足够应付多数日志或配置文件。
常见错误是忽略换行符截断:默认 Scanner 会去掉每行末尾的 \n 或 \r\n,如果业务需要保留原始行尾,得手动拼接;另外扫描失败时(比如超长行)Err() 不为 nil,但很多人只检查 Scan() 返回值,漏掉错误。
- 用
scanner := bufio.NewScanner(file)初始化后,务必在循环结束后调用scanner.Err()判断是否因 I/O 错误提前终止 - 若需正则匹配,别在每次循环里重复
regexp.Compile,提前编译好复用 - 匹配到目标行后,用
scanner.Text()获取内容(不含换行符),如需带换行符,可改用scanner.Bytes()+ 手动追加\n
用 strings.Contains 还是 strings.HasPrefix?看匹配意图
简单子串查找用 strings.Contains 最直观,但容易误匹配——比如搜 "error" 可能命中 "warning" 里的 "error";如果目标是日志级别或配置项开头(如 "log.level="),优先用 strings.HasPrefix 或 strings.FieldsFunc 分词后再比对。
-
strings.Contains(line, "timeout")适合模糊定位,但需确认上下文不会产生歧义 -
strings.HasPrefix(line, "# ")更安全地提取注释行,避免匹配到"#something"中间出现的# - 若要忽略大小写,别用
strings.ToLower全转再查(额外分配),改用strings.EqualFold或正则加(?i)标志
遇到空行、BOM、编码异常怎么办
Windows 生成的文本可能带 UTF-8 BOM(\xef\xbb\xbf),Go 的 bufio.Scanner 不自动剥离,会导致首行匹配失败;空行(只有换行符)会被读成空字符串,不是 nil,容易被 if line == "" 漏掉;GBK/GB2312 文件不转换直接读会是乱码。
立即学习“go语言免费学习笔记(深入)”;
- 打开文件后先用
bytes.TrimPrefix去 BOM:content := bytes.TrimPrefix(b, []byte("\xef\xbb\xbf")),或者用golang.org/x/text/encoding包转码 - 判断空行别只靠
len(line) == 0,用strings.TrimSpace(line) == ""更可靠 - 若明确知道文件是 GBK,别硬读,用
encoding.RegisterEncoding注册编码器,再 wrapos.File
性能敏感场景:避免重复分配和正则回溯
每行都 strings.Split(line, ":") 或 regexp.FindStringSubmatch 会频繁分配小对象;复杂正则(如 .* 嵌套)在长行上可能指数级回溯,拖慢整个读取过程。
- 提取固定分隔符字段,用
strings.Index+line[i:j]切片,零分配 - 正则只在必要时用,且模式尽量锚定:
^ERROR:.*$比ERROR:更快,因为减少回溯范围 - 如果只是提取第 N 行(比如配置文件第 5 行),别全扫,用
for i := 0; i 提前退出
真正难的不是读哪一行,而是搞清你要的是“精确匹配”还是“近似定位”,以及文件到底有没有隐藏的编码或格式陷阱。动手前先 head -n 5 file.txt | hexdump -C 看一眼开头几字节,省掉后面八成调试时间。



















