
本文介绍如何使用 Go 语言结合正则表达式与 bufio.Scanner,在文本文件中定位符合日期格式(如 Sat 08 Aug 2015)的行,并高效提取其后的连续五行内容。
本文介绍如何使用 go 语言结合正则表达式与 `bufio.scanner`,在文本文件中定位符合日期格式(如 `sat 08 aug 2015`)的行,并高效提取其后的连续五行内容。
在 Go 中处理逐行文本并基于模式触发后续行读取时,关键在于控制扫描器的状态流——bufio.Scanner 是单向迭代器,一旦调用 scanner.Scan(),指针即前移至下一行。因此,当检测到匹配行后,需在同一个 for 循环体内连续调用 scanner.Scan() 五次,直接读取并输出后续内容。
以下为完整、健壮的实现方案(已优化错误处理、正则严谨性及可维护性):
package main
import (
"bufio"
"fmt"
"log"
"os"
"regexp"
)
func main() {
// 改进正则:避免 [aA-zZ] 这类错误范围(ASCII 中 'Z' < 'a',会匹配非字母字符)
// 正确写法:(?i)^[a-z]{3}\s\d{2}\s[a-z]{3}\s\d{4}$
r := regexp.MustCompile(`(?i)^[a-z]{3}\s\d{2}\s[a-z]{3}\s\d{4}$`)
file, err := os.Open("test.txt")
if err != nil {
log.Fatalf("无法打开文件: %v", err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if r.MatchString(line) {
fmt.Println(line) // 打印匹配的日期行
// 提取紧随其后的 5 行
for i := 0; i < 5; i++ {
if !scanner.Scan() {
// 文件提前结束(不足 5 行)
if err := scanner.Err(); err != nil {
log.Printf("读取过程中发生错误: %v", err)
}
log.Printf("警告:在日期 '%s' 后仅能读取 %d 行,已到达文件末尾", line, i)
break
}
fmt.Println(scanner.Text())
}
}
}
if err := scanner.Err(); err != nil {
log.Fatalf("扫描文件时出错: %v", err)
}
}✅ 关键改进说明:
- 正则修复:原 [aA-zZ] 实际等价于 [a-zA-Z] 的错误写法(因 ASCII 码序中 'Z'(90)< 'a'(97),[aA-zZ] 会意外包含 [aA-\x5a] 和 [\x7a-Z] 等非法区间)。现改用 (?i)^[a-z]{3}...$,语义清晰且安全。
- 错误防御:每次 scanner.Scan() 前检查返回值,避免在 EOF 或 I/O 错误时 panic;对不足 5 行的情况给出明确日志提示。
- 资源安全:defer file.Close() 确保文件句柄及时释放。
⚠️ 注意事项:
- scanner.Scan() 在遇到 EOF 时返回 false,此时 scanner.Text() 返回上一次成功读取的内容(不会更新),因此必须在调用 Scan() 后立即检查结果再使用 Text()。
- 若需将提取结果结构化(如解析为比赛记录 struct),建议在捕获五行后进一步按字段切分(如使用 strings.TrimSpace 和 strings.Fields 处理缩进数据)。
- 对超大文件,该方案内存友好(流式读取,无全文加载),但若需随机回溯(如反向查找前一行),应改用 os.File.Seek + 行号索引,或切换为 bufio.Reader 手动控制。
通过本方法,你可稳定、高效地实现“模式触发 + 固定偏移提取”的典型日志/报表解析任务。


















