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, 只输出了一个 "."
posted @ 2011-10-09 22:30  walfud  阅读(2639)  评论(0编辑  收藏  举报