本文介绍使用 bufio.scanner 逐行读取文件并匹配目标字符串,同时准确返回所有匹配行的行号;支持多行匹配、错误处理,并可扩展至并发批量文件搜索。
本文介绍使用 bufio.scanner 逐行读取文件并匹配目标字符串,同时准确返回所有匹配行的行号;支持多行匹配、错误处理,并可扩展至并发批量文件搜索。
在 Go 中,直接将整个文件读入内存(如用 ioutil.ReadFile)虽简单,但对大文件不友好,且无法天然获取行号。更健壮、内存友好的做法是流式逐行扫描——使用 bufio.Scanner 配合手动行计数,既能控制资源消耗,又能精确标识每一处匹配的位置。
以下是一个完整、可复用的函数示例,用于查找指定字符串在文件中的所有出现位置及对应行号:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// FindStringInFile 返回文件中所有匹配字符串的行号(从1开始计数)
func FindStringInFile(path, target string) ([]int, error) {
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open %s: %w", path, err)
}
defer f.Close()
var lines []int
scanner := bufio.NewScanner(f)
lineNum := 1
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, target) {
lines = append(lines, lineNum)
}
lineNum++
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error reading %s: %w", path, err)
}
return lines, nil
}
// 示例用法
func main() {
matches, err := FindStringInFile("example.txt", "ERROR")
if err != nil {
panic(err)
}
for _, ln := range matches {
fmt.Printf("Found at line %d\n", ln)
}
}
✅ 关键优势说明:
- ✅ 内存安全:不加载全文到内存,适合 GB 级日志等大文件;
- ✅ 行号精准:lineNum 严格按 Scan() 成功次数递增,与实际文本行一一对应;
- ✅ 支持多次匹配:自动收集全部命中行号,而非仅返回首个;
- ✅ 错误可追溯:scanner.Err() 捕获 I/O 异常(如权限不足、磁盘中断),便于诊断;
- ✅ 易于扩展:若需搜索数千个文件,可结合 goroutine + worker pool(参考 Go by Example 的 Worker Pools)实现并发加速,避免阻塞主线程。
⚠️ 注意事项:
- strings.Contains 区分大小写;如需忽略大小写,改用 strings.Contains(strings.ToLower(line), strings.ToLower(target));
- 若需正则匹配或高亮上下文(如前后3行),建议改用 regexp 包并封装为独立模块;
- Windows 换行符(\r\n)会被 Scanner 自动处理,无需额外兼容;
- 对超长单行(>64KB),Scanner 默认会报错;可通过 scanner.Buffer(make([]byte, 64*1024), 1
掌握此模式后,你不仅能精准定位字符串,还能以此为基础构建日志分析器、代码审计工具或配置检查脚本——简洁、可靠、地道的 Go 风格实践。











