3、正则表达式入门:元字符、字符集、分组与捕获、贪婪与非贪婪模式
日志分析这件事,说白了就是在一堆文本里找到你想要的那几行。正则表达式就是干这个的利器。我刚开始接触日志时,觉得用 grep 加几个关键词就够了,后来发现日志量一上来,光靠关键词根本抓不住那些隐藏的模式。嗯,正则这东西,值得花点时间搞明白。
3.1 元字符:正则的字母表
元字符是正则表达式的基本构件。它们不匹配字面字符,而是匹配一类位置或特征。我个人习惯把元字符分成三组来记:
| 类别 | 元字符 | 含义 | 我在项目中遇到过 |
|---|---|---|---|
| 位置锚点 | ^ $ |
行首 / 行尾 | 用 ^ERROR 过滤错误日志开头行 |
| 单字符通配 | . |
匹配任意单个字符(除换行) | 匹配 IP 地址时 \d+\.\d+\.\d+\.\d+ 里的点必须转义 |
| 量词 | * + ? |
0次或多次 / 1次或多次 / 0次或1次 | 解析时间戳时 \d{2}:\d{2}:\d{2} 比 \d+:\d+:\d+ 更精确 |
| 转义 | \ |
将元字符转义为字面量 | 匹配文件路径时 C:\\Users\\ 双反斜杠容易忘 |
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} 比 .* 靠谱得多。我曾经用 .* 抓 IP,结果把整行日志都吞了,排查了半天才发现是贪婪匹配搞的鬼。
3.2 字符集:精准匹配一组字符
字符集用方括号 [] 表示,匹配括号内任意一个字符。你想想看,如果我想匹配「error」或「Error」,用 [Ee]rror 就搞定了,比写两个分支清爽得多。
字符集里还有一些快捷写法:
[0-9]等价于\d,匹配数字[a-zA-Z]匹配所有大小写字母[^0-9]匹配非数字(脱字符在字符集内表示取反)
避坑指南: 我曾经在解析 Nginx 日志时,用 [^ ]+ 来匹配「非空格字符序列」,结果因为日志里混了制表符,匹配全乱了。后来改成 [^\s]+ 才稳定。记住:\s 包含空格、制表符、换行,而空格只是其中之一。
3.3 分组与捕获:提取关键信息
分组用圆括号 () 表示。它有两个作用:一是改变优先级,二是捕获匹配的内容供后续使用。
举个例子,假设日志行是这样的:
2025-03-21 14:30:22 ERROR [user_id=12345] Connection timeout
我想提取日期和用户 ID,可以这样写:
(\d{4}-\d{2}-\d{2}) .* \[user_id=(\d+)\]
这里有两个捕获组:$1 是日期,$2 是用户 ID。在 sed 或 awk 里可以直接引用它们。
(?:...) 非捕获组。我在处理每天几千万行的日志时,把捕获组改成非捕获组后,处理时间缩短了 15%。别小看这点优化。
3.4 贪婪与非贪婪:量词的两种性格
这是新手最容易踩的坑。默认情况下,量词 *、+、{n,m} 都是贪婪的——它们会尽可能多地匹配字符。
举个例子:
文本:<div>内容1</div><div>内容2</div>
正则:<div>.*</div>
匹配结果:<div>内容1</div><div>内容2</div> (整个字符串)
为什么会这样?因为 .* 贪婪地吃掉了中间所有字符,直到最后一个 </div> 才停下。
解决办法是在量词后面加 ?,变成非贪婪模式:
正则:<div>.*?</div>
匹配结果:<div>内容1</div> (只匹配第一个)
非贪婪模式会尽可能少地匹配,找到第一个符合条件的就收手。
:.*?: 比 :.*: 安全得多。我曾经在生产环境用贪婪匹配解析 JSON 日志,结果一条日志里嵌套了多层花括号,正则直接炸了内存。从那以后,我养成了「非贪婪优先」的习惯。
3.5 知识体系总览
下面这张图把本章的核心知识点串起来了。你可以把它当作正则入门的速查地图。
3.6 实战:用正则解析一行日志
光说不练假把式。我们拿一条真实的 Nginx 访问日志来练手:
192.168.1.10 - - [21/Mar/2025:14:30:22 +0800] "GET /api/users HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
我想提取四个字段:IP、时间、请求路径、状态码。正则可以这样写:
^(\S+) .* \[(\d{2}/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} [+\-]\d{4})\] "(\w+) (\S+) .*" (\d+) .*
捕获组对应关系:
$1→192.168.1.10(IP)$2→21/Mar/2025:14:30:22 +0800(时间)$3→GET(方法)$4→/api/users(路径)$5→200(状态码)
.* 来跳过中间字段。如果日志格式有变化,比如多了个字段,.* 可能会匹配到不该匹配的内容。更健壮的做法是用 [^ ]* 或 [^"]* 来限定范围。我在维护一个老系统时,就因为 .* 太宽松,导致日志格式微调后正则全部失效,那次教训让我学会了「能窄就别宽」。
3.7 本章小结
正则表达式入门其实就四个核心概念:元字符是砖瓦,字符集是筛选器,分组是提取工具,贪婪与非贪婪是匹配策略。你不需要一次性记住所有元字符,先掌握 ^ $ . * + ? [] () 这几个,配合 \d \w \s 就能处理 80% 的日志场景。