您的位置:首页 > 其它

正则表达式的应用

2012-09-21 22:57 465 查看

1.使用m//匹配

我们曾经将模式放在一对正斜线(//)里面,如/fred/。这是m//(模式匹配)的一种简写。同qw//操作一样,可以使用任何

成对的分隔符。因此,可以使用m(fred), m<fred>, m{fred}, m[fred],或者m,fred,, m!fred!, m^fred^,其它非成对的分隔符也

可以◆。

如果使用正斜线(/)作为分隔符,则可以省略掉前面的m。由于Perler 喜欢少输入字符,因此大多数模式使用的是正斜线,

如/fred/。

使用一个不会在模式中出现的字符作为分隔符◆。如果想写一个匹配web URL 开头部分的模式,你可能使用/http:\/\//来匹

配http://。但如果使用m%http://%将更易于阅读,书写,维护,以及调试。使用花括号({})作为分隔符也是很平常的。

如果你使用的是专为程序员设计的文本编辑器,由于它能自动从开花括号跳到闭花括号,这对于维护代码将非常有用。

2.可选的修饰符

不区分大小写:/i

匹配任何字符:/s

默认情况下,点(.)不匹配换行符,这对于“单行中查找”的问题能很好解决。如果你的字符串中有换行符,并希望点(.)能匹

配它们,那可以使用/s 这个修饰符。它将模式中点(.)◆的行为变成同字符类[\d\D]的行为类似:可以匹配任何字符,包括换

行符。从下例中可见其区别:

◆如果你想改变其中的一部分,但不是全部,那可以将此部分用[\d\D]代替

$_ = “I saw Barney\ndown at the bowing alley\nwith Fred\nlast night.\n”;

if(/Barney.*Fred/s){

print “That string mentions Fred after Barney!\n”;

}

如果不使用/s,那么上述模式将不能被匹配上,因为这两个字符不在同一行中。

添加空格:/x

如果在一个模式中需使用不止一个修饰符,可以一个接着一个。其顺序是不重要的:

if(/barney.*fred/is/){ # /i 和/s
print “That string mentions Fred after Barney!\n”;
}
下面是包含注释的版本:
if (m{
barney #小个子小伙
.* #可以包含任何字符
fred #嗓门大的小伙
}six) { #修饰符包括包括/s, /i, /x
print “That string mentions Fred after Barney!\n”;
}


3.锚定

符号^(脱字字符◆)表示在字符串的开头进行匹配,而符号$则表示在结尾◆。因此,模式/^fred/只匹配字符串的开头部分;

它不会匹配上manfred man。而/rock$/只在结尾处匹配;其不会匹配上knute rockne。

有时,想同时使用这两个锚定来确保匹配整个字符串。一个经常使用的例子是/^\s*$/,它将匹配一个空行(blank line)。这

里的“blank(空白)”,可以是空白符,如制表符,空格等,它们是不可见的

锚定不仅仅针对字符串的两头。词界锚定,\b,是针对单词使用的。如/\bfred\b/可以匹配上单词fred,但不能匹配frederick,

alfred, man fred mann。这同字处理软件中的“全字匹配(match whole words only)”是类似的。

词界锚定是非常有用的,如果不希望cat 匹配上delicatessen,而dog 匹配上boondoggle,或者fish 匹配上selfishness。有时,

你只想要一个词界锚定,当使用/\bhunt/ 将匹配上像hunt, hunting, hunter,这样的单词,但不会匹配shunt,而/stone\b/将匹

配sandstone, flintstone,而不能匹配上capstones。

非词界锚定为\B。它将在任何非\b 匹配的点上进行匹配。因此,模式/\bsearch\B/将匹配searches, searching, searched, 但不能

匹配search,或者researching。

4.绑定操作符,=~

对$_进行匹配只是默认的行为,使用绑定操作符(=~)将告诉Perl 将右边的模式在左边的字符串上进行匹配,而非对$_匹配。

第一次见到绑定操作符(binding operator:=~),可能觉得它有些像赋值操作符,但它不是。其含义是:“这个模式默认将对

$_进行匹配,但此时将对左边的字符串进行匹配”。如果没有绑定操作符,则此表达式将对$_匹配。

print “Do you like Perl? ”;
my $likes_perl = (<STDIN> =~ /\byes\b/i);
… #Times passes…
if($likes_perl){
print “You said earlier that you like Perl, So… \n”;
…
}


由于绑定操作有非常高的优先级,因此,模式测试部分的括号不是必需的,下面的代码和上面代码的含义是一样的。它将
测试部分的结果(而非行输入)返回给变量$likes_perl:
my $likes_perl = <STDIN> =~ /\byes\b/i;


5.模式内的内插

正则表达式可以被内插,如同双引号字符串一样。。这允许我们快速地写出类似grep 的程序:

#! /usr/bin/perl –w
my $what = “larry”;
while(<>){
if(/^($what)/){ #在字符串前面进行匹配
print “We saw $what in beginning of $_;”;
}


当程序运行时,模式将被$what 的值代替。在本例中,它同使用/^(larry)/是一样的,在行的开头处查找larry。

如果没有在程序中具体指出$what 的值,也可以在命令行中输入,然后使用参数@ARGV:

my $what = shift @ARGV;


如果命令行中第一个参数是fred|barney,则模式为/^(fred|barney)/:在行首查找fred 或者barney◆。括号(在查找larry 的

例子中不是必须的)是非常重要的,如果没有它,则在行首查找fred,或在此行中任意位置查找barney。

6.匹配变量

我们曾经在模式中使用过括号,使用括号是由于它可以将模式的某一部分组合起来。同时括号也会引起正则表达式分配新

的内存块。这些内存含有括号中的模式所匹配的字符串。如果有不止一对括号,那就不止一块内存块。每一个内存块内有

一段字符串,而非模式的一部分。

由于这些变量含有字符串,那它们是标量变量;在Perl 中,它们具有像$1, $2 这样的名字。变量个数同模式中括号对数的个

数是相同的。如$4 是指第四对括号所匹配的字符串◆。

这些匹配变量(match variables)是组成正则表达式强大功能的重要部分,它允许取出相应的字符串:

$_ = “Hello there, neighbor”;
if(/\s(\w+),/){ #空格和逗号之间的词
print “the word was $1\n”; #the word was there
}


也可以一次使用多个:

$_ = “Hello there, neighbor”;
if(/(\S+) (\S+), (\S+)/){
print “words were $1 $2 $3”;
}


其输出为words were Hello there neighbor。注意输出中没有逗号。因为第二块内存中没有逗号。使用这种技术,可以选择

我们感兴趣的部分。

匹配变量可能是空的◆,如果其没有被匹配上。也就是说,匹配变量的值可能为空串:

my $dino = "I fear that I'll be extinct after 1000 years.";
if ($dino =~ /(\d*) years/) {
print "That said '$1' years.\n"; # 1000
}
my $dino = "I fear that I'll be extinct after a few millions years.";
if ($dino =~ /(\d*) years/) {
print "That said '$1' years.\n"; # 空串
}


6.1 内存值的保存

这些匹配变量的值会保持不变,直到下一个模式成功匹配为止◆。也就是说,一个没有匹配成功的模式将不会改变内存中

相应的值,但一个匹配上的模式将重写此内存。这明确的告诉你,不要随意的使用这些变量,除非明确知道它们匹配正确;

否则,你可能得到上个模式匹配的结果。下面的例子(不好的例子)本意是输出被$wilma 变量匹配的字符串。但如果匹配

失败,它将输出$1 中以前所匹配上的字符串。

$wilma =~ /(\w+)/; #不好,没有检测匹配的结果
print “Wilma’s word was $1… or was it?\n”;
这也是为什么模式匹配几乎都在if 和while 循环的条件判断出现的原因:
if ($wilma =~ /(\w+)/){
print “Wilma’s word was $1.\n”;
} else {
print “Wilma doesn’t have a word.\n”;
}


由于内存中的值不会一直保留,那应当在模式匹配后尽快地(几行内)使用像$1 这样的变量。如果维护人员在正则表达式和

使用$1 的表达式之间加入了新的正则表达式,那此时$1 的值为第二个匹配的结果,而非第一个。由于这个理由,如果要在

后面使用这个变量的值,应当将其拷贝到普通变量之中。这样做同时也可以使程序更易于阅读:

if($wilma =~ /(\w+)/){
my $wilma_word = $1;
…
}


6.2自动匹配变量

还有三个匹配变量,你可以不花什么代价的使用它们◆,无论这些模式是否有括号。这是个好消息;但同时,这些变量的

名字相当古怪。这里提到的三个变量名为: $&, $`, $'。它们看起来相当奇怪,但无论怎样,这就是其名

字◆。匹配上的那部分字符串将自动存储在$&之中。

if(“Hello there, neigbor”=~ /\s(\w+),/){

print “That actually matched ‘$&’.\n”;

}

匹配的部分是“there,”(空格,单词,和一个逗号)。变量$1 中的值为there,而$&为整个被匹配的部分。

匹配部分的前一部分存放在$`之中,后一部分被存到$'。另一种说法是,$`中含有正则表达式引擎在匹配成功前所找到的变

量,而$'为此模式还没有匹配的剩余部分。如果将这三个变量放在一起,你将得到原始字符串:

if (“Hello there, neighbor”=~ /\S(\w+),/){
pirnt “That was ($`)($&)($’)”;
}


输出的消息为(Hello)( there,)( neighbor),为这三个自动匹配变量的值。三个变量的值可能是空的,和之前数字匹配变量的例

子一样。它们和数字匹配变量有相同的作用域。通常,在下次成功匹配前其值不变。

7.一般的数量词

模式中的数量词表示前面项重复的次数。你已经见过三个数量词:*, +, ?。如果这三个数量词仍不能满足你的要求,那可以

使用花括号({}),花括号中有2 个数字,由逗号隔开,表示前面一项允许重复的次数。

模式/a{5,15}/将匹配5 个到15 个a 中的任意一个(包括5,和15)。如果a 出现了3 次,则次数太少,而不能匹配上;如果

出现5 次,则匹配上了;如果出现10 次,仍然匹配上。如果出现20 次,仍将匹配上,前15 个将匹配上。

如果省略掉第二个数字(逗号留下),现在没有上限了。因此,/(fred){3,}/将在一行中有3 个或者更多个fred 时匹配上(fred

之间不允许有其它字符,包括空格)。这里没有上限,因此如果有88 个fred,仍将匹配上。

如果除了上限数字外,逗号也被省略了,那将匹配确定的次数:/\w{8}/将严格的匹配8 个word(字母,数字,下划线)(可

能被其中一个长字符串部分所匹配上)。/,{5}chameleon/将匹配“,,,,,,chmeleon”。

前面介绍过的三个数量词是简写形式。星号(*)等同于{0,},表示0 个或多个。加号(+)等同于{1,},表示1 个或多个。而问

号(?)则等同于{0,1}。在实际程序中,很少使用花括号的数量词,前面介绍的三个数量词(*,+,?)基本已能应付。

8 .优先级

正则表达式中的这些元字符,可能让你觉得如果没有一个表很难记住它们的关系。这就需要一个优先级的表,表明模式中

哪一部分应当结合在一起。这和操作符的优先级表有些不同,正则表达式的优先级表更简单,只有4 个级别。本节,将回

顾Perl 在模式中使用的所有的元字符。

1.在此优先级表的最顶端是括号:(()),在分组和引用内存值的时候使用。括号内部的任何部分比括号外的部分结合更紧

密。

2.第二级是数量词。这里有星号(*), 加号(+),问号(?)以及由花括号表示的数量词,如{5,15}, {3, }, {5}等。它们通常

和前一项元素结合。

3.第三级的是锚定和序列(sequence)。锚定包括(^) 表明字符串的开头,($)表明结尾,(\b)词界符,(\B)非词界符。序列(一

个元素紧接着一个元素)实际上是一种操作,虽然它没有使用元字符。这段话的含义是一个单词中的字母结合更紧密,就

像锚定紧贴字母一样。

4.优先级最低的是竖线(|),表示或。由于其优先级最低,它通常将模式划分成几个部分。它在优先级最底端是因为我们希

望像|/fred|barney/里面的字母比或(|)结合更紧密。如果或(|)的优先级比序列的优先级更高,那么,上述模式的含义是匹配fre,

接着是d 或者b, 然后是arney。因此,或(|)的优先级最低,字母序列的优先级要高些。

除了优先级表外,还有被称为原子(atoms)的东西,它们组成模式最基本的块。它们是单个字符,字符类,以及后引用

(backreference)。

当需要解析复杂的正则表达式时,你应当像Perl 那样使用优先级表来理解它。

例如,/^fred|barney$/很可能并不是程序员想要的模式。由于竖线(|)的优先级最低;它将上述模式分成了两部分。这个模式

在或者开头是fred,或者结尾是barney 时匹配上。很可能程序员想要的是(^(fred|barney)$),这将匹配只有fred 或者只有barney

的行◆。那/(wilma|pebbles?)/呢?问号也对前面的元素有效◆,它将匹配wilma, pebbles, 或者pebble,可能是某个长字符串

的一部分(因为没有锚定)。

◆当然,结尾处可以有换行符,我们在早期讨论$锚定时提到过。

◆因为数量词?和字母s 结合更紧密

模式/^(\w+)\s+(\w+)$/将匹配那些含有一个“词(word)”,一些空白,另一个“词(word)”,之外没有其它符号的行。

它将匹配上像fred flintstone 这样的行。模式中的括号是不需要的,当然它们可以将匹配上的字符串存放在$1,$2 之中。

当解析复杂的正则表达式时,括号将有助于你区分优先级。可以这样做,但请记住,如果使用了括号,则这些括号所匹配

的字符串将放在相应的内存变量之中($1,$2...)之中。当添加新的括号时也可能需要改变这些以前的编号◆。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: