Regex in Perl
regex literal
代表正则文字, 就是 m/regex/ 部分中的 regex, 这部分有自己的解析规则. 用 Perl 的行话就是 "表示正则含义的双引号字符串(regx-aware double-quoted string)", 及处理后传递给正则引擎的结果.
正则文字支持的特性:
1. 变量插值. 如 m/$str/, 其中的 $str 会被扩展成变量内容, 但 $str 内部的变量不会被展开. 但不支持散列内插. (Perl 本身就不支持散列内插).
2. 大小写转换. 如 m/\l$str/ 或 m/\U$str\E/ 等. 其等价函数是 \l => lcfirst(), \u => ucfirst(), \L .. \E => lc(), \U .. \E => uc().
3. 文字文本. 如 m/\Q$str\E/. 其等价函数是 qutoemeta()
3.1 希望匹配的是此模式内所包含的变量所代表的字符, 而不是该变量中的正则表达式.
3.2 对于不能识别的转义字符, \Q .. \E 将会导致警告 "unrecognized escape".
4. 命名的 Unicode 字符
其他
在 match 操作中, 当分隔符是 '/' 或 '?' 时, 可省略 m/regex/ 中的 m.
不能对本作用域的局部变量调用 local, 但是可以对其更外层作用域的局部变量调用 local.
local 是行为, 而 my 既是行为也是声明.
Symbol | Effect | Default |
$' | 匹配之前的文本 | |
$& | 匹配的文本 | 等价于 substr($text, $-[0], $+[0] - $-[0]) |
$` | 匹配之后的文本 | |
$1 | 第一组匹配文本 | |
$2 | 第二组匹配文本 | |
$^N | 括号最后结束的一组匹配文本 | |
$+ | 编号最大的一组匹配文本 | |
$" | 数组内插到标量时, 默认的分隔符 | 空格 |
@- | 各个捕获的起始位置 (0 是匹配的起始) | |
@+ | 各个捕获的结束位置后的一位 (0 是匹配的结束后的一位) |
(\w+) 与 (\w)+
如 "\n\n\nq1w2e3r4\t\t\t" =~ (\w+)
两者匹配的内容是相同的. 区别在于捕获括号中的内容不同. 前者将捕获整个可匹配的字串, 因此, 例子中 $1 的值是 "q1w2e3r4". 后者则会迭代捕获新的内容, 因此 $1 只保留最后一个字符 "4".
(x?) 与 (x)?
在 (x?) 中, 捕获必然发生, 但是其捕获的内容可以为空字符, 也就是 "". 但是在 (x)? 中情况则不同, 因为捕获不是必须发生, 因此 $1 的值可能会出现 def 或 x.
qr 运算符生成 regex 对象
主要用来把正则表达式封装为一个单元, 构建更强大的正则表达式, 以提高效率。
regex 对象一旦封装完成, 对应的匹配模式就不能更改了。即: 不能向已构建的正则对象添加新的模式。
my $regex = qr/$var/ # 支持普通变量内插
my $string = qr/$regex/ # 也支持嵌套的 regex 对象内插
在 scalar context 中, 正则对象被认为是一个含有模式说明符的正则表达式字符串。
match 运算符 ( m/ regex /xismgc )
在 scalar context 中, =~ 返回 0 或 1. 代表不匹配和匹配。
在 list context 中, =~ 返回捕获所匹配的内容。
#!/usr/bin/perl -w
use 5.010 ;
# 拆分 IP 地址
my $ip = "192.168.1.1" ;
my ($ip1, $ip2, $ip3, $ip4) = $ip =~ m/(\d+)/g ; # list context
say $ip1 ; # 192
say $ip2 ; # 168
say $ip3 ; # 1
say $ip4 ; # 1
字符串中的 “当前位置” 属性与 /g 修饰符每个字符串都有一个 “当前位置” 属性, 表示在正则表达式应用到这个字符串时, 驱动程序的开始锚点。可通过 pos() 设定与获得。任何创建和修改字符串的行为都会造成 “当前位置”被重置。即,“当前位置” = undef, 从而指向字符串的起始。
/g 修饰符的作用就是:
如果本次成功匹配, 则将 ”当前位置“ 设置为成功匹配结束的位置。
如果匹配失败,则将重置 “当前位置” 。即, 将 “当前位置” 设置为 undef, 此时意为传动装置将从字符串开头开始驱动。
#!/usr/bin/perl -w
use 5.010 ;
my $str = "192.168.1.1" ;
$str =~ m/\w+/g ; # 将匹配 192
my $pos = pos($str) ;
say $pos ; # 3/gc 修饰符
/c 修饰符指: 当匹配失败后, 不修改 “当前位置” 属性。
当指定 /g 修饰符时才可指定 /c 修饰符。所以常常说 /gc。
\G 修饰符告诉传动装置, 不要启动驱动过程, 如果在此处(即字符串的 “当前位置” 属性)无法匹配, 则宣告失败。
不能和全局性多选结构一起使用, 否则结果是未定义的。
substitution 运算符 ( s/ regex / replacement /xismge )
match 可以省略 m, 但是 substitution 不能省略 s。
substitution 无论在 scalar context 中还是 list context 中, 返回值都表示替换的次数, 或空字串。
/g 修饰符
全局替换。
/e 修饰符
replacement 在 scalar context 中重新求值。#!/usr/bin/perl -w
use 5.010 ;
my $str = "current: time." ;
$str =~ s/time/localtime/eg ; # 对 localtime 进行求值
say $str ; # current: Thu Oct 13 10:08:31 2011.
多次使用 /e 修饰符, 表示对 replacement 进行多次求值。#!/usr/bin/perl -w
use 5.010 ;
my $str = "Three times evaluate: var" ;
my ($var1, $var2, $var3) = ('$var2', '$var3', 3) ;
$str =~ s/var/$var1/eeeg ;
say $str ; # Three times evaluate: 3
Perl 的专有特性
动态正则结构: (??{ perl code })
每次遇到这种结构, 就会立即执行 perl code 代码. 执行的结果将立即被作用于当前的匹配.
perl code 的返回值会被认为是正则文字的一部分.
"3xxxyy" =~ m/(\d)(??{"x{$1}"})/ ; # 可以匹配 "3xxx"
应用:
匹配任意深度的嵌套.
#!/usr/bin/perl -
use 5.010 ;
my $x = "int fun(int a, int (*pfun)(char b))" ;
my $level_n ; # 先声明, 下面才能使用
$level_n = qr/\((?>[^()]+|(??{$level_n}))*\)/ ;
$x =~ m/$level_n/ ;
say $& ;
解释:
第一阶段首先在 fun( 的开括号处遇到匹配,
接着进入固化分组, [^()]+ 匹配了 int fun(int a, int (*pfun)(char b))
第二阶段
再遇到开括号, 因为 int ( 这个开括号无法匹配 [^()]。 因此进入第一层多选结构 (??{$level_n}) 的判定。 这个分支是动态正则结构, 因此会立即进入动态结构进行匹配操作。
此时动态正则结构 (??{$level_n}) 展开为自身 “\((?>[^()]+|(??{$level_n}))*\)”, 并对 "(*pfun)(char b))" 进行匹配。
当 (*pfun 遇到 $level_n 中的 “\((?>[^()]+|(??{$level_n}))*\)” 时, 得到匹配, 因此继续向后进行匹配。
(*pfun 因 “\((?>[^()]+|(??{$level_n}))*\)” 得到匹配。
此时, 遇到 "(*pfun)" 中的 “)”. 且正则文字中的 [^()]+ 无法匹配, 并进入第二层 “(??{$level_n})” 多选分支, 但是字符 “)” 对于多选分支展开后的正则文字 “\((?>[^()]+|(??{$level_n}))*\)” 依然得不到匹配, 因此退出第二层多选分支。
驱动程序前进, “)” 遇到第一层的 “\((?>[^()]+|(??{$level_n}))*\)”, 得到匹配。 完成并退出第一层多选分支。
第三阶段
此后同理。
嵌套代码结构: (?{ arbitrary perl code })
每次遇到此结构, 立即执行 perl 代码.
此结构较 "动态正则结构" 更为通用, 因为不需要 arbitrary perl code 的返回值作为正则文字的部分进行匹配. 在调试中十分常见.
#!/usr/bin/perl -
use 5.010 ;
my $x = "have a nice day" ;
$x =~ m{(?{print "."})day$}x ; # 应该会输出 13 个 "." 号, 但是在 windows 下使用 strawberry perl, 只输出了一个 "."