分类:
2010-08-21 23:52:53
正则表达式有三种形式:匹配、替换和转换。
在表 9-1 中列有三种正则表达式运算符。
接下来对每一个表达式给出详尽解释。
匹配:m/
替换:s/
·转换:tr/
注意转换 通过使用=~(用英语讲:does,与“进行匹配”同)和!~(英语:doesn't,与“不匹配”同)把这些表达式捆绑到标量上。作为这种类型的例子,下面我们给出六个示例正则表达式及相应的定义: $scalarName =~ s/a/b; # substitute the character a for b, and return true if this can happern $scalarName =~ m/a; # does the scalar $scalarName have an a in it? $scalarName =~ tr/A-Z/a-z/; # translate all capital letter with lower case ones, and return ture if this happens $scalarName !~ s/a/b/; # substitute the character a for b, and return false if this indeed happens. $scalarName !~ m/a/; # does the scalar $scalarName match the character a? Return false if it does. $scalarName !~ tr/0-9/a-j/; # translate the digits for the letters a thru j, and return false if this happens. 如果我们输入像 horned toad =~ m/toad/ 这样的代码,则出现图 9-1 所示情况: 另外,如果读者正在对特定变量 $_ 进行匹配(读者可能在while循环,map或grep中使用),则可以不用!~和=~。因而,以下所有代码都会合法: my @elemente = (' al' ,' a2' ,' a3' ,' a4' ,' a5' ); foreach (@elements) {s/a/b/;} 程序使 @elements 等于b1,b2.b3,b4,b5。另外: while(<$FD>) {print if (m/ERBOR/);} 打印所有包含 error 字符串的行: if (grep(/pattern/, @lines)) {print " the variable \@lines has pattern in it!\n";} 打印所有包含模式pattern内容的行,这直接引入下一原则。 9.3.2 原则2 正则表达式仅在标量上匹配。 注意这里标量的重要性,如果读者试一试如下代码: @arrayName = (' variablel', 'variable2'); @arrayName =~ m/variable/; # looks for ' variable' in the array? No! use grep instead 那么@arrayName匹配不成功! @arrayName被Perl解释为2,于是这意味着读者在输入: ' 2' =~ m/variable/; 至少讲这不能给出预想的结果。如果读者想这样做,输人为: grep(m/variable/, @arrayName); 该函数通过@arrayName中的每一个元素进行循环,返回(在标量环境中)匹配的次数,同时在数组环境中返回匹配元素的实际列表。 9.3.3 原则3 对于给定的模式串,正则表达式只匹配最早出现的匹配项。匹配时缺省一次只匹配或替换一次。 这个原则使用称为“回溯”的过程指出如何匹配一个给定的字符串;如果发现了一个局部匹配进而找到使该匹配无效的东西,正则表达式在字符串中“回溯”最小的可能数量,这个数量的字符要保证不丢失任何匹配。 对于理解正则表达式正在做什么,这个原则是最有帮助的一个,同时不需要与Perl一样的形式来理解它正在做什么。假定有如下模式: ' Silly people do silly things if in silly moods' 同时想匹配如下模式:‘ ' silly moods' 那么正则表达式引擎匹配silly,接着遇到people的P,至此,正则表达式引擎知道第一个silly不匹配,于是正则表达式引擎移到 P 且继续寻求匹配。它接着遇到第二个silly,于是来匹配moods。然而得到的是字母 t(在thing中),于是移到 things中的 t 处,继续进行匹配。当引擎遇到第三个silly并且尽力匹配moods时,匹配成功,匹配最后完成。所发生的情况如图 9-2 所示。 当我们遇到通配符时回溯将变得更加重要。如果在同一正则表达式中有几个通配符,且所有的通配符交织在一起,那么这里就有病态情形出现,在这种情形下,回溯变得非常昂贵。看如下表达式: : $line = m/expression.*matching.*could.*be.*very.*expensive.*/ .* 代表一个通配符,它意味着“匹配任意字符(换行符除外)零次或多次”。这个过程有可能花很长时间;如果在未匹配过的字符串末尾有可能匹配,那么引擎将发狂地回溯。为得到这方面的更多信息,请留意关于通配符方面的原则。 如果读者发现类似于上面的情形,那么通配符需将正则表达式分解成小功部分。换句话讲,简化自己的正则表达式。 9.3.4 原则4 正则表达式能够处理双引号字符串所能处理的任意和全部的字符。 在s///运算符(s/*//),或者m//运算符m/*/的第一个分隔区,位于其中的条目确实能像双引号字符串一样对待(带有一些额外的附加功能,名义上的特殊正则表达式字符!后面描述)。读者可用他们进行内插: $variable = ' TEST' ; $a =~ m/${variable}aha/; 和: $a = " ${variable}aha" ; 二者都指向同一字符串:前者在$a中匹配字符串TESTaha.后者把$a设置为字符串 TESTaha。因为正则表达式处理双引号字符串能处理的每个字符,所以可以执行下列操作: $expression = ' hello'; @arrayName = (' elem1', ' elem2'); $variable =~ m/$expression/; # this equals m/hello/; 在这里,我们简单地把$expression扩展为hello而得到m/hello/。这个技巧也可用于数组: $variable =~ m/@arrayName/; # this equals m/elem1 elem2/; 在这里,表达式等价于 m/elem1 elem2/。如果特殊变量$"被设置为 |.则表达式将等价于 m/elem | elem2/,正如我们看到的,它匹配字符串的elem或者elem2。这种方法也可应用于特殊字符: $variable =~ m/\x01\27/; # match binary character x01, and # octal character 27. $variable =~ s/\t\t\t//; # substitute three tabs for three spaces. 实际上,这里所讨论的除极少数例外以外,Perl处理在m//中的过程的确像处理在双引号中的一样。但是有例外:有某些对正则表达式引擎有明确意义的字符。那么,如果想匹配类似于正斜杠(/)或者园括引(())这样的字符会发生什么呢?这些字符对正则表达式引取有特殊意义:因而不能使用如下语句: $variable=~ m//usr/local/bin/; # matches /usr/local/bin? NO! SYNTAX ERROR 因为Perl将把/解释为正则表达式的结束标记。这里有三种方法去匹配类似于上述特殊字符的方法。第一种方法是利用反料杠来“转义”想匹配的任意特殊字符一包括反斜杠。因而刚才给出的例子可变为: $path =~ m/\/usr\/local\/bin/; 该程序尽力匹配 $path中的/usr/local/bin。第二种方法是使用一个不同的正则表达式字符。如果有许多字符要匹配,那么使用反斜杠则会变得很难看(路径字符尤其不好)。 幸运的是,Perl以一种合成形式来确决这个问题。因为在当读者输入m//或s///时需要给每个/加反斜杠,所以正则表达式允许读者将正则表达式的定界符(/)改为自己喜欢的任意字符。例如,我们可以使用双引号(")来避免大量的反斜杠: $variable =~ m"/usr/local/bin"; # Note the quotation marks. $variable =~ m"\"help\""; # If you are going to match quotation # marks, you need to backslash them here. (as per\") $variable =~ S" $variable" $variable"; # works in s///too. 出于好的初衷,我们在本书的前几章使用了这一约定。如果使用"作为读者的正则表达式字符,那么在用起来时它充当了好的记忆法,记住在这里所处理的实际上是字符串的变相反插入;否则,引号就远不如斜杠常用。 Perl允许使用{}()[]来书写正则表达式: $variable =~ m{this works well with vi or emacs because the parens bounce}; $variable =~ m(this also works well); $variable =~ s(substitute pattern) {for this pattern}sg; 这一原则对我们处理多行正则表达式非常方便。因为在这里可以不用圆括号,所以读者可以开始把表达式作为“微型函数”对待(如果读者有像emacs或vi这样的合理的智能编辑器),换句话讲,读者可在表达式的开始和结尾部分之间往返。 第三种方法是利用函数quotemeta()来自动地加反斜杠。如果输入如下代码: $variable =~ m" $scalar"; 则$scalar将为被插且被转变为标量的值。这里有一个警告:任何一个特殊字符都将被正则表达式引擎影响,并且可能引起语法错误。因此,如果标量为: $scalar = " ({"; 那么输入如下代码: $variabie =~ m" $scalar" ; 就等价于是说:$variable =~ m"({",而这是一个运行时语法错误。如果表达式为如下形式: $scalar = quotemeta(' ({'); 则表达式会使$scalar变为\(\{,且把$scalar替换为: $variable =~ m" \(\{" ; 这样才可以匹配到读者愿意匹配的字符串({。并不真正是一个正则表达式,但是对于用正则表达式难于处理的数据常使用它来进行操纵。因此,tr/[0-9]/9876543210.组成1223456789,987654321等字符串。