Perl的正则表达式(二)

用m//进行匹配

我们常常用//来表示模式,比如/fred/,但事实上,这是m/pattern/的简写,我们可以选用任何的定界符配合m来表示模式。例如:

m(fred),m{fred},m[fred]

如果你使用双斜线作为定界符,可以省略开头的m。但是,你应该明智的选择模式中不会出现的字符作为定界符,例如网址匹配时,你可能要用/http:\/\//来匹配最开始的"http://",其实可以用另外一个版本:m{^http://},更容易维护。

模式匹配修饰符

模式匹配修饰符是追加在模式表达式末尾定界符后面的字母,用来改变默认的匹配行为。

  • /i 进行大小写无关的匹配
  • /s匹配任意字符 默认情况下,点号(.)无法匹配换行符,这对大多数单行匹配的情况是合适的。但如果字符串含有换行符,而你希望点号能匹配这些换行符,那么/s能完成这个任务。
$_ = "I saw Barney\ndown at the bowling alley\nwith Fred\nlast night.\n";
if(/Barney.*Fred/s){
    print "that string menstions fred after barney!\n";
}

没有/s的话,上面的匹配会失败,因为两个名字并不在同一行。

  • /x加入空白符 这个修饰符允许我们在模式里随意加上空白符,从而使它更易阅读。如果我们真的希望匹配到空白符,可以通过转义字符来实现:\s,\s*,\s+
  • 组合选项修饰符 如果需要对单词匹配使用多项修饰符,只需要把他们接在一起写在模式末尾,不用在意先后顺序。

锚位

默认情况下,如果给定模式不匹配字符串的开头,就会顺移到下一个字符继续尝试,而通过锚位,我们可以让模式仅在字符串指定位置匹配。

  • \A 匹配字符串的绝对开头。
  • \z 匹配字符串的绝对末尾。
  • \Z 行末锚位,允许后面出现换行符。
  • ^ 与\A相同
  • $ 与\z相同

在^,$后面加上/m修饰符之后,可以改变他们的匹配行为,分别变成行首锚位和行末锚位。

/\A\s*\Z/   匹配空行
/^barney/m
  • 单词锚位 \b是单词锚位,它匹配任何单词的首尾,不过这里的单词指的是一组\w字符构成的字符集,也就是由英文字母,数字,下划线组成的字符串。单词边界锚位非常有用,它保证我们不会意外的在delicateness中找到cat,在boondggle中找到dog;有时候你会只用到一个单词边界锚位,/\bhunt/来匹配hunt,hunting,而不会是shunt。

绑定操作符=~

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

print "do you love perl?"
my $like_perl = <STDIN> =~/\byes\b/i;
if($like_perl){
    print "you said you like perl!\n";
    }

模式中的内插

正则表达式内部可以进行双引号形式的内插:

my $what = "larry";
while(<>){
    if(\A($what)){
        print "....";   }
}

在这里它等效于/\A(larry)/.

捕获变量

圆括号出现的地方一般都会触发正则表达式引擎捕获匹配到的字符串,我们可以通过反向引用\4来取得这些捕获内容,也可以在匹配操作结束后立即通过相应的捕获变量取得这些内容。虽然反向引用\1和捕获变量\$1的内容是一样的,但它们并不是同一个事物的两种名称:\1是模式匹配期间得到的结果,而\$4是模式匹配结束后对得到的捕获内容的索引。

可以说,捕获变量是正则表达式无比强大的重要原因之一,因为有了它,我们才得以拥有提取字符串中某些特定部分的能力:

my $dino = "I fear that i will be extinct after 1000 years ."
if($dino =~ /([0-9]*) years/){
    print "that said $1 years.\n";
    }

如果给出的字符串不符合模式的要求,那么得到的捕获变量为空。

捕获变量的存续期

捕获变量通常会存活到下一次成功匹配位置,也就是说,失败的匹配不会改动上次成功匹配时捕获的内容,而成功的匹配会将他们的值重置。因此,模式匹配总是出现在if或者while条件表达式里:

if($wilma =~ /([a-zA-Z]+)/){
    print "wilma's word was $1.\n";
    }else{
    print "wilma does not have a word.\n";
    }
}

既然这些捕获内容不会永远存留,通常只会在模式匹配之后的数行之内使用,如果想在更多的地方使用,一般会将它复制到普通变量里。

不捕获模式

只要是圆括号都会捕获部分的匹配字符串到捕获变量中,但某些情况下我们想关闭这个功能,而仅仅是用它来进行分组。

my $names = 'fred and barney';
if($names =~ m/(\w+) (and|or) (\w+)/){
    print"I saw $1 and $2";
    }
                 VS
if($names =~ m/(\w+) (?:and|or) (\w+)/){
    print"I saw $1 and $2";
    }

上面的例子中我们想捕获的是and 或者or前后的名字,但是不得不为and ,or保留一个分组,因此实际上我们应该使用\$1,$3作为捕获变量,然而如果改用不捕获模式,就可以不用因为加入了分组括号以后修改捕获变量名。

命名捕获

我们可以为捕获到的内容命名,并保存到一个特殊的哈希%+里。我们还可以用\g{label}来反向引用。

my $names = 'fred and barney';
if($names =~ m/(?<name1>\w+) (and|or) (?<name2>\w+)/){
    print"I saw $+{name1} and $+{name2}";
    }

my $names = 'fred flintstone and barney flintstone';
if($names =~ m/(?<last_name>\w+) and \w+ \g{last_name}/){
    print"I saw $+{last_name}";
    }

自动捕获变量

Larry提供了3个免费的捕获变量,就算不加捕获圆括号也能使用。\$`,\$&,\$'分别表示匹配区段前的内容,匹配区段,匹配区段后的内容。因此,如果将这三个区段连接起来,肯定能得到原始字符串。

if("hello there, neighbor" =~ /\s(\w+),/){
    print "that was ($`)($&)($').\n";
}

我们经常用自动捕获变量来进行模式测试,来检查模式匹配的范围,开始的位置是否符合预期,或者是否根本无法匹配。下面这个程序经常会被用来测试模式:

while(<>){
    chomp;
    if(/YOUR_PATTERN_GOES_HERE/){
    print "Matched: |$`<$&>$'|\n";
    }else{
    print "no matched: |$_|\n";
    }
}

通用量词

常见的量词有*,?,+,上篇blog中已经讲过。 此外,你还可以用{}定制具体的重复次数范围。

/a{5,15}/;[3,15]
/(fred){3,};3次以上,并且fred之间不能有空格

优先级

  • 圆括号: (...), (?:...), (?
  • 量词 : a*,a+,a?,a{n,m},它们会跟它们前面的条目紧密相连
  • 锚位和序列: word,abc,^,$,\A,\z,\Z,序列(彼此相邻的条目)也是操作符,也就是说单词里字母之间的紧密程度和锚位与字母之间的紧密程度是相同的。
  • 择一竖线: a|b|c例如/fred|barney/会被解释成'fred' or 'barney',而不会被解释成'fre' 'd' or 'b' 'arney'.
  • 原子: a,[abc],\d,\1
/(wilma|pebbles?)/;?只对s有用

在尝试理解一个很复杂的模式时,试着加上一些圆括号会对弄清优先级有好处,但是,圆括号同时也会有捕获的效果,因此,建议尽量可能用非捕获圆括号来分组。

Comments !