日志巡检规则引擎开发与告警系统集成 - 第4节
正则表达式入门:元字符、量词、分组、环视
正则表达式这东西,说白了就是"用符号来描述文本模式"。我刚开始接触时也觉得它像天书,但一旦理解了核心的几个概念,你会发现它其实就那么点东西。本章我们从四个核心概念入手——元字符、量词、分组、环视,最后用 IP 地址和时间戳的匹配来收尾。
本章知识体系: 元字符(基础单元)→ 量词(重复控制)→ 分组(结构组织)→ 环视(位置断言)→ 实战组合
元字符:模式的「字母表」
元字符是正则表达式中最基本、最原子的单位。它们代表某一类字符,而不是字面量本身。
最常用的元字符
| 元字符 | 含义 | 示例 | 匹配结果 |
|---|---|---|---|
. |
匹配任意单个字符(除换行) | a.b |
aab、a1b、a#b |
\d |
匹配一个数字(0-9) | \d{3} |
123、456 |
\w |
匹配单词字符(字母、数字、下划线) | \w+ |
abc_123、hello |
\s |
匹配空白字符(空格、制表符、换行) | a\sb |
a b、a\tb |
\b |
匹配单词边界 | \bword\b |
精确匹配 word |
我的习惯: 在日志巡检中,
\d 和 \w 是我用得最多的两个元字符。比如匹配错误码 ERR-\d{4},或者匹配日志中的标识符 \w+。记住,元字符就是你的「字母表」,先熟悉它们。
量词:控制重复的「节拍器」
量词决定了前面的元素「重复多少次」。没有量词,你只能匹配固定长度的模式,那实用性就大打折扣了。
六种基本量词
| 量词 | 含义 | 示例 | 可匹配 | 不匹配 |
|---|---|---|---|---|
* |
0 次或多次 | ab*c |
ac、abc、abbc |
adc |
+ |
1 次或多次 | ab+c |
abc、abbc |
ac |
? |
0 次或 1 次 | ab?c |
ac、abc |
abbc |
{n} |
恰好 n 次 | \d{3} |
123 |
12、1234 |
{n,} |
至少 n 次 | \d{2,} |
12、123、1234 |
1 |
{n,m} |
n 到 m 次 | \d{2,4} |
12、123、1234 |
1、12345 |
我曾经踩过的坑: 用
* 时一定要小心贪婪匹配。比如 .* 会匹配到行尾,有时候根本不是你想要的结果。我习惯在可能失控的地方加上 ? 变成非贪婪模式,比如 .*?。这招在日志切片时特别管用。
分组:把模式「打包」起来
分组用 () 把一段模式包起来,相当于给它一个「容器」。分组不只是为了好看,它有几个非常实用的功能。
三种分组场景
- 捕获分组
():把匹配的内容存下来,后面可以用\1、\2引用。比如(\w+)\s+\1能匹配重复的单词hello hello。 - 非捕获分组
(?:):只分组,不捕获。性能更好,且不污染编号。我习惯在只需要分组逻辑时优先用这个。 - 反向引用
\1\2:引用前面第 n 个分组捕获的内容。在日志巡检中,我经常用它来匹配对称结构,比如 XML 标签的成对出现。
我个人的建议: 当你只需要把一组字符当成整体来用量词修饰时,一定要用
(?:) 而非 ()。比如 (?:\d{3}\.){3}\d{3} 匹配 IP 的前三段,用非捕获分组既清晰又高效。
环视:不消费字符的「侦察兵」
环视是个很酷的概念——它只判断「位置」是否符合条件,但不消费字符。也就是说,它不把匹配到的字符吞掉,只是看一眼。这在实际匹配中非常有用。
四种环视
| 类型 | 写法 | 含义 | 示例 |
|---|---|---|---|
| 正向先行 | (?=...) |
后面跟着指定模式 | \d(?=px) 匹配 16px 中的 6 |
| 负向先行 | (?!...) |
后面不跟着指定模式 | \d(?!px) 匹配 16pt 中的 6 |
| 正向后顾 | (?<=...) |
前面是指定模式 | (?<=\$)\d+ 匹配 $100 中的 100 |
| 负向后顾 | (? |
前面不是指定模式 | (? 匹配 |
避坑指南: 我曾经在解析日志中的键值对时,想匹配
key=value 中的 value,但又不想要等号。用 (?<==)\w+ 一次搞定。环视的威力就在于「只看不拿」。
实战一:匹配 IP 地址
IP 地址看起来规整,其实有些陷阱。标准的 IPv4 地址是 xxx.xxx.xxx.xxx,每个段范围 0-255。
简单版匹配
# 只匹配格式,不验证范围
\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b
这个版本能匹配 192.168.1.1,但它也会匹配 999.999.999.999——这在日志里基本是无效 IP。
精确版匹配
# 精确验证每个段 0-255
\b(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b
拆解一下:
25[0-5] 匹配 250-255,2[0-4]\d 匹配 200-249,[01]?\d\d? 匹配 0-199。三个分支用 | 组合,确保每个段都在合法范围内。我在生产环境的日志巡检里,用的就是这种精确版本。
实战二:匹配时间戳
日志里最常见的时间戳格式是 YYYY-MM-DD HH:MM:SS。我们先从基础版本开始,再逐步加固。
基础匹配
\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}
这个版本能匹配 2025-01-15 14:30:00,但也会匹配 2025-13-99 25:61:70 这种不存在的日期时间。
精确匹配(含范围验证)
# 月份 01-12,日期 01-31,小时 00-23,分钟/秒 00-59
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]) (?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d
| 字段 | 正则片段 | 说明 |
|---|---|---|
| 年 | \d{4} |
简单匹配四位数字 |
| 月 | (?:0[1-9]|1[0-2]) |
01-12 |
| 日 | (?:0[1-9]|[12]\d|3[01]) |
01-31 |
| 时 | (?:[01]\d|2[0-3]) |
00-23 |
| 分 | [0-5]\d |
00-59 |
| 秒 | [0-5]\d |
00-59 |
我曾经遇过的坑: 直接用
\d{2} 匹配月份和日期,结果把 2025-13-99 也纳入了。后来排查了半天才发现是正则不够严。记住,日志巡检中「不该匹配的不要匹配」和「该匹配的一定匹配上」同样重要。
本章小结
到这里,正则的四个核心概念就全部过了一遍。元字符是砖瓦,量词是手脚,分组是容器,环视是眼睛。把这四个组合起来,你就能写出足够精确的日志匹配模式。IP 地址和时间戳的实战就是最好的练习场——先跑通简单版,再上精确版,一步步来,别急着一步到位。