4.1 正则基础


4.1 正则基础

正则表达式是描述文本形状的迷你语言:普通字符匹配自身,元字符描述"什么类型、重复几次、锚在哪"。Perl 的 // 就是匹配操作符,无需任何导入。

第一个匹配

my $line = '10.0.0.5 - - [12/Aug/2026:03:14:51] "GET /api/order HTTP/1.1" 200 12ms'; if ($line =~ /GET/) { print "这是一条 GET 请求\n"; }

$line =~ /模式/ 返回真假,守卫写法 next unless $line =~ /ERROR/; 是日志脚本的日常。

元字符速查

符号 含义 日志示例
\d \s \w 数字、空白、单词字符 状态码 \d{3}
. 任意字符(换行除外) 时间戳粗匹配
+ * ? 1+、0+、0或1 次 \d+ 一串数字
{m,n} m 到 n 次 IP 每段 \d{1,3}
^ $ 行首、行尾锚 ^ERROR 行首才认
[...] 字符集合任选一个 [45]\d\d 4xx/5xx
(...) 分组与捕获 提取字段的主力
`\ `

实战组合:

$line =~ /^(\d{1,3}(?:\.\d{1,3}){3})/; # 行首 IP $line =~ m{"(GET|POST) (\S+)}; # 请求方法与路径 $line =~ /\[(\d{2})\/(\w{3})\/(\d{4})/; # 日期三段

两点值得注意:URL 里常有斜杠,用 m{...} 换定界符省得转义;(?:...) 是只分组不捕获,避免占用捕获组编号。

命中与不命中的写法惯律

if ($line =~ /" (\d{3}) /) { $status = $1; }

未命中时 $1 保留上一次的旧值——这是新手统计出离谱数字的经典来源。要么把匹配与取值写在同一个 if 里,要么用 4.2 节的列表上下文一次取全。

💡 关键直觉:正则的阅读单位不是字符而是"字段"。写之前先用自然语言描述字段边界("从引号后到空格"),再翻译成符号,命中率会高得多。

锚点误用现场:一次真实的统计事故

理解锚点最好的方式是看一次翻车。需求:统计 ERROR 日志里某服务的出现次数,有人写了 /error/(没加锚、没加大小写意识),结果把 DEBUG 行里的 "errorHandler initialized" 也数了进去,报出的错误量是真实的四倍。修复后:

next unless /^ERROR\b/; # 行首锚 + 单词边界 $svc_count{$2}++ if /\] (\w+):/; # 第二个字段才是服务名

三处细节各挡住一类误报:^ 挡住行中部巧合;\b 挡住 ERRORLOG 这类前缀撞车;显式定位"冒号前的单词"挡住同名子串。给初学者的推论是:每写一个模式,先问"它会在哪误命中"——把日志里最吵的 DEBUG 行拿来试一遍,误报当场现形。

贪婪与非贪婪:量词的第二副面孔

+* 默认贪婪(尽量多吃),加 ? 变非贪婪(尽量少吃)。提取 HTML 式尖括号内容是最直观的对照:

my $s = '<b>粗体</b>普通<b>再来</b>'; my @greedy = $s =~ /<b>(.+)<\/b>/g; # 贪婪:"粗体</b>普通<b>再来" 一整条 my @lazy = $s =~ /<b>(.+?)<\/b>/g; # 非贪婪:"粗体" "再来" 各一条

日志字段的引号内容同理:/"(.+?)"/ 拿到的是第一对引号里的内容,贪婪版会把两对引号之间连皮带骨抓走。经验法则只有一句:分隔符之间的内容,量词一律写非贪婪;要"吃到行尾"才用贪婪(如 ^(.*):\s+(\d+)$ 里第一个 .*)。

修饰符与定界符的完整口袋

除了 m{} 换定界符,五个常用修饰符值得一次记全:

$blk =~ /^ERROR/m; # m:多行模式,^ $ 逐行生效 $text =~ /name=".+?"/s; # s:让 . 也匹配换行,跨行提取必备 $cmd =~ /usage/i; # i:忽略大小写 $all =~ /(\d+ms)/g; # g:列表上下文下取出全部命中而非首个 /x # x:模式里允许空白与注释,长模式分段写

x 修饰符是长模式的救星,把一坨符号排版成可读的段落:

$line =~ m{ ^(\d{1,3}(?:\.\d{1,3}){3}) # 1: 客户端 IP \s+\S+\s+\S+ # 身份两列(跳过) \[([^\]]+)\] # 2: 时间戳整体 "\w+\s+(\S+?)\s+[^"]*" # 3: 请求路径 }x;

注释即文档,半年后回看不用重新解码。工坊规矩:超过 60 个字符的模式,必须换 x 风格重写。

最后给一张"字段到模式"的对照速查,覆盖日志解析九成场景,写新模式前先来这里找形状:

IP 地址 ^(\d{1,3}(?:\.\d{1,3}){3}) 日期时间 \[(\d{2})/(\w{3})/(\d{4}):(\d{2}:\d{2}:\d{2}) 请求行 "(\w+) (\S+) HTTP/[\d.]+" 状态码 (\d{3}) 字节数/耗时 (\d+)(ms|s)? 带引号内容 "([^"]*)" # 排除型,防贪婪越界 键值对 (\w+)=("[^"]*"|\S+) UUID/trace ([0-9a-f-]{36})

右列每个形状都值得抄进自己的 snippets 文件。用时贴过来改定界符即可,比每次现场推导又快又稳——熟练工和新手的差距,往往就是这张表有没有。搭配记忆的还有一个习惯:每收编一个新形状,用一行真实日志当注释附在旁边,形状与样本互相印证,下次改动立刻有对照物。

本节要点回顾

  • =~ // 是匹配操作符,守卫式 next if /.../ 最常用
  • \d \s \w 加量词能覆盖日志字段的九成描述需求
  • ^ $ 锚住行首行尾,防止匹配到行中部的巧合片段
  • (?:...) 只分组不捕获,不污染捕获组编号
  • $1 在未命中时保留旧值,取值必须与判定同处

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U