正则表达式中Unicode属性

Unicode字符不只是简单的字节序列,除了字节组合之外,它还附带着属性信息。所以除了匹配字符本身以外,我们还能根据字符的属性来匹配:

if (/\p{Space}/) { # 总共有26个不同字符带此属性 
    print "The string has some whitespace.\n"; 
} 
                                                                                                                                                                           
if (/\p{Digit}/) { # 总共有411个不同的数字字符 
    print "The string has some whitespace.\n"; 
} 
                                                                                                                                                                           
if (/\p{Hex}/) { # 匹配十六进制字符集[0-9A-Fa-f] 
    print "The string has some whitespace.\n"; 
} 
                                                                                                                                                                           
if (/\P{Space}/) { # 大写的'P'表示反向选择 
    print "The string has some whitespace.\n"; 
}


元字符

点号(.)可以匹配任意一个字符,除了换行符'\n'。

反斜线(\)会使接下来的下一个字符失去特殊作用,仅仅代表下一个字符本身。


量词

星号(*)用来匹配前面的条目零次或多次。例如,'.*'可以匹配换行符以外的任意字符串。

加号(+)用来匹配前面的条目一次或多次。

问号(?)用来匹配前面的条目零次或一次。


模式分组和反向引用

在正则表达式中,圆括号‘()’的作用是对字符串分组。例如,模式/fred+/会匹配像fredddddddd这样的字符串,而/(fred)+/可以匹配fredfredfred这样的字符串。

通过反向引用,我们可以构成一些有趣的模式

$_ = "abba"; 
if (/(.)\1/) { # 匹配'bb' 
    print "It matched same character next to it self!\n"; 
}

一个正则表达式中可以有多个模式分组,甚至是嵌套,那么该如何区分哪个括号是第几组呢?其实只要一次点算左括号的序号就行了——

$_ = "yabba dabba doo"; 
if (/y((.)(.)\3\2) d\1/) { 
    print "It matched!\n"' 
}

如果反向引用后跟着的是数字怎么办呢?(比如\111引用的是\1、\11还是\111呢)其实Perl会尽可能地创建最多数量的反向引用。不过从Perl 5.10开始支持一种新的反向引用写法:\g{N}的形式。

use 5.010 
                                                                                                                                                       
$_ = "aa11bb";  
if (/(.)\g{1}11/) { # N代表想要反向引用的组号。 
    print "It matched\n"; 
} 
                                                                                                                                                       
$_ = "xaa11bb";  
if (/(.)(.)\g{-11}11/) { # 反向引用的组号可以是相对位置 
    print "It matched\n"; 
}


择一匹配

竖线(|)通常可以读成“或”,意思是要么匹配左边的内容,要么匹配右边的内容。

例如可以使用/fred(|\t)+barney/这样的模式来匹配fred和barney之间出现一次或以上空格、制表符或两者混合的字符串。


字符集

字符集指的是一组可能出现的字符,通过写在方括号([ ])内表示。它只匹配单个字符,但可以是字符集中列出的任何一个。比如[abcwxyz]可以匹配这7个字符中的任意一个。为方便起见,你也可以用连字符“-”表示始末范围,如[a-cw-z]。

有时候,指定字符集以外的字符会比指定字符集内的字符更容易。可以在字符集开头的地方加上脱字符(caret,^)来表示这些字符除外。

有些字符集出现的频率非常高,所以我们给它们设定了简写形式。例如:

\d 表示任意一个数字 (注意,Perl5.6之前它严格等同于字符集[0-9],但它现在还能匹配比较少见的其他Unicode字符集中的数字)

\s 能匹配任意空白符,效果上等同于Unicode属性\p{Space} (Perl5.6之前仅能匹配换页符、水平制表符tab、换行符、回车符和空格符本身)

\w 一直被称为“word”字符,尽管它匹配的并不是严格意义上的单词字符。ASCII语义下它匹配的是[a-zA-Z0-9_]

在从ASCII到Unicode转变的过程中,怎么区分前后不同意义的字符集成了一个需要解决的问题。因此Perl 5.14引入了一种新的修饰符\a,写在正则表达式末尾,表示按照ASCII语义展开,例如 /HAL-[\d]+/a 可以匹配“HAL-9000”这样的字符串


反义简写

有时候你也许需要指定以上几种简写以外的字符,例如[^\d]、[^\w]或是[^\s]这样的模式。为此我们引入了它们的大写版本表示否定意义,如\D、\W、\S。

有种比较特别的复合字符集[\d\D],表示任意数字或非数字字符。也就是说,它能匹配任意字符,甚至包括换行符!(“.”只能匹配换行符以外的所有字符)


用m/ /进行匹配

当使用“/”作为正则表达式的定界符时,可以省略前面的模式匹配操作符“m”。就像“qw//”操作符一样,我们可以选择任何成对的定界符。当然我们应该明智地选择成对的或者模式中不会出现的字符作为定界符。


模式匹配修饰符

/i表示进行大小写无关的匹配

/s 可以使“.”匹配任意字符,包括换行符

/x 允许模式中加入空白符以便于阅读,不过原来表示空白的字符就必须加上转义字符“/”

print "Would you like to play a game?"; 
chomp($_ = <STDIN>); 
if (/yes/i) { # 大小写无关的匹配 
    print "In that case, I recommended taht you go bowling.\n"; 
} 
                                                                                                                                            
$= "I saw Barney\ndown at the bowling alley\nwith Fed\nlast night.\n"; 
if (/Barney.*Fred/s) { # 匹配成功 
    print "That string mentions Fred after Barney!\n"; 
} 
                                                                                                                                            
/-?[0-9]+\.?[0-9]*/ # 挤在一起很难看清什么意思 
/ -? [0-9]+ \.? [0-9]* /x #加入空白后好多了

当然如果我们同时需要多个修饰符,只要把它们接在一起放在模式末尾就可以了。(不用在意先后次序)


模式匹配中的字符解释方式

/a 表示ASCII方式

/u 表示采取Unicode方式

/l 表示遵从本地化语言的设定


锚位

Perl 5中\A锚位匹配字符串的绝对开头,\z匹配字符串的绝对末尾,\Z是行末锚位,它允许后面出现换行符。

m{\Ahttps?://}i # 匹配字符串是否以https开头 
m{\.png\z}i #匹配以.png结束的字符串 
m{\.png\Z}i #匹配以.png结束的行

在Perl 4 中,所用的表示字符串开头锚位的是脱字符(^),用于表示字符串结尾锚位的是$。这种写法到了Perl 5依然可用,不过已经演化成了行首和行末锚位。

如果需要对多行内容进行匹配怎么办呢?你可以用$锚位和/m修饰符表示对多行内容进行匹配。

除此之外,还有“\b”单词边界锚位,它能匹配任何单词的首尾。“\B”锚位用于匹配其他所有\b不能匹配的位置。


绑定操作符=~

默认情况下模式匹配的操作对象是$_,绑定操作符拿右边的模式来匹配左边的字符串。

my $some_other = "I dream of betty rubble."; 
if ($some_other =~ /\brub/) { # 匹配成功 
    print "Aye, there's the rub.\n"; 
}


捕获变量

圆括号“()”出现的地方一般都会触发正则表达式引擎捕获匹配到的字符串。捕获组会把匹配圆括号中模式的字符串保存到相应的地方。与反向引用类似,正则表达式根据“(”出现的次序一次对捕获组编号为1、2、3……

$_ = "Hello there, neighbor"; 
if (/\s([a-zA-Z]+),/) { # 捕获空白符和逗号之间的单词 
    print "the word was $1\n"; # 打印the word was there 
}

这些捕获变量通常在下次成功匹配前会一直有效,所以$1之类的捕获变量只应该在模式匹配后的数行内使用。

有时候圆括号仅仅使用来分组,我们需要关掉这个功能

if (/(?:bronto)saurus (steak|burger)/) { 
    print "Fred wants a $i\n"; 
}

为了彻底解决维护诸如$1、$2这样的变量带来的麻烦,不用每次修改模式都从新把圆括号数一遍,Perl 5.10增加了对捕获内容直接命名的写法。最终捕获到的内容会保存在特殊哈希%+里面,其中的键就是在捕获时用的特殊标签,对应的值则是捕获的字符串。

use 5.010; 
                                                                                                                    
my $names = 'Ferd or Barney'; 
if ($name =~ m/(?<name1>\w+) (?:and|or) (?<name2>\w+)/) { 
    say "I saw $+{name1} and $+{name2}"; #结果输出 I saw Fred and Barney 
}

有三个免费的捕获变量,就算不加圆括号也能使用。$&里保存的是匹配成功的整个区段;$`里面保存匹配区段之前的内容哦;$'保存匹配区段之后的内容

if ("Hello there, neighbor" =~ /\s(\w+),/) { 
    print "That was ($`)($&)($').\n"; # 程序运行时会把字符串显示为(Hello)(there,)(neighbor) 
}

如果你使用的是Perl 5.10或以上的版本,那么就更加方便了。修饰符/p灰针对特定的正则表达式开启类似的自动捕获变量,但它们的名字不再是$`、$&、$',而是用${^PREMATCH}、${^MATCH}、${^POSTMATCH}来表示。


通用量词

到目前为止,我们已经见过三个量词*、+、?如果这三个量词都不符合需要,还可以使用花括号的形式制定具体的重复次数。

因此,模式/a{5,15}/可匹配重复出现5到15次的字母a。

所以*相当于{0,};+相当于{1,};?相当于{0,1}。


优先级

正则表达式特性示例
圆括号(分组或捕获)(...), (?:...), (?<LABEL>...)
量词a*, a+, a?, a{m,n}
锚位和序列abc, ^, $, \A, \b, \Z, \z
择一竖线a|b|c
原子a, [abc], \d, \1, \g{2}


一个模式测试程序

#! /usr/bin/perl 
while (<>) { # 每次读入一行输入 
    chomp; 
    if (/YOUR_PATTERN_GOES_HERE/) { 
        print "Matched: |$`<$&>$'|\n"; #特殊捕获变量 
    } else { 
        print "No match: |$_|\n"; 
    } 
}