Perl正则表达式讲解
? Perl正则表达式讲解
?
在正则表达式中用圆括号括住某模式就是告诉解释器“嗨,我希望保存那个数据。” Perl解释器再应请求,且将查找到的匹配保存在一系列特珠的变量中($1,$2,$3…$65536),这些变量可用来查询第一个、第二个、第三个等等圆括号匹配,这些变量于是可以通过查看相应的变量或在数组环境下对正则表达式进行求值而且进行访问。例如: $text = " this matches ' THIS' not 'THAT' "; $text =~ m" ('TH..' )"; print " $1n"; 在这里,字柳HIS被打印出来 —— Perl已经将它们保存在$1中,以后再打印$1。 然而,该例子揭示了更多内容,例如: 1)通配符(字符点(.)匹配任意字符)。如果THIS不在字符串中,模式(TH..)将欣然匹配 THAT。 2)正则表达式匹配一行上出现的第一处模式。THIS因为首先出现,所以被匹配。同时,按缺省regexp行为,THIS将总是被匹配的第一个字符串。(可以用修饰符改变缺省值,详细情况稍后介绍)。 图 9-3 表示了这一匹配过程如何进行。 在图 9-3 中每个圆括号与自已的数字变量一道运行。 这里有更多的例子: $text = ' This is an example of backreferences'; ($example,$backreferences) = ($text =~ m" (example).*(backreferences)" ); 这里又用了通配符来分开两个文本字符串$example和$backreferences。这些字符串存放在$1和$2中,且随后立即赋给$example和$backrefercences。该过程在图9—4中说明。 然而应注意的是仅当文本字符串匹配时,给$example和$bacbreference赋值的过程才发生。当文本字符串不匹配时,$example和 backreferences是空的。这里有更好的同样的例子,该例子包含在if语句中,仅在匹配时打印$1和$2。 if ($text =~ m" (example).*(back)") { print $1; # prints ' example' -- since the first parens match the text example. print $2; # prints ' back' -- since the second parens match the text back } 这样,如果正则表达式根本不匹配将发生什么?如果使用下面的模式: $text = ' This is an example of backreferences'; $text =~ s" (examplar).*(back)" doesn't work"; print $1; $1因正则表达式匹配不成功而不能被赋值。更重要地,Perl不会告诉读者它没有给$1赋任 何值。最后一例展示了关于正则表达式的两点重耍内容: 1)正则表达式是“要么全部要么什么也没有”的处理,只因为back字符串在模式内才能匹 配,所以: This is an example of backreferences' 并不意味着整个表达式达到匹配。因为exemplar不在字符串中,因而替换失败。 2)如果正则表达式失败,反向引用不能得到赋值。因此,不能肯定将打印出什么内容。当跟踪逻辑问题时,这就是让人吃惊的原因;且经常是Perl gotcha。$1只是一个正则变量,并且(与Perl语法相反)如果正则表达式失败则反向引用不被设置为“空白”。有人认为这是一个缺陷,然而另有人认为这是一个特色。不过,当分析下面的代码时第二点变得非常明显。 1 $a = ' bedbugs bite'; 2 $a =~ m" (bedbug)"; # sets $1 to be bedbug. 3 4 $b = ' this is nasty'; 5 $b =~ m" (nasti)"; # does NOT set $1 (nasti is not in ' this is nasty' ). 6 # BUT $1 is still set to bedbug! 7 print $1; # prints ' bedbug'. 在这种情况下,$1为字符串bedbug,因为第5行的匹配失败!如果希望得到nasti,好吧,那是自己的问题。这种Perl化的行为可能让人不知所措。考虑的是自己要当心。 3.使用反向引用的一般构造法 如果想避免这种很平常的缺陷(读者想得到一个匹配,但是没有得到并且用前面的匹配为 替代而结束),在把反向引用赋给变量时只要应用下列三个构造法之一: 1)短路方法。核查匹配,如果匹配发生,此时且只有此时用' &&'进行赋值,例如: ($scalarName =~ m" (nasti)" ) {$matched =?$1;} 2)if子句。将匹配放于if子句中,如果if子句为真,此时且只有此时才为模式赋值。 if ($scalarName =~ m" (nasti)" ) {$matched = $1;} else {print "$scalarName didn't match"; } 3)直接赋值。因为可以直接把正则表达式赋给一个数值,所以可始终利用这一点。 ($match1,$match2) = ($scalarName =~ m" (regexp1).*(regexp2)" ); 读者的所有模式的匹配代码看起来应该与前述三个例子中的一个相似。缺少这些形式,那么就是在没有安全保证的条件下进行编码。如果读者从不想有这种类型的错误的话,那么这些形式将节省读者的大量时间。 4.在正则表达式中使用反向引用 当希望使用s" " "运算符或者用m" "运算符对一些复杂模式进行匹配很困难时,Perl提供了读者应意识到的有用功能。这个功能就是反向引用可以用于正则表达式自身。换句话说,如果用圆括号括住一组字符,那么就可以在正则表达式结束之前使用反向引用。如果想在s" " "的第二部分(带下划线)中使用反向引用,那么要使用语法$1,$2等。如果想在m" "或者s" " "的第一部分(带下划线)使用反向引用,那么使用语法12等。下面是一些例子: $string = ' far out'; $string =~ s "(far)(out)" $2 $1"; # This makes string ' out far'. 我们在该例中只是将单词far out转换为out far。 $string = ' sample examples'; if ($string =~ m" (amp..) ex1") {print " MATCHES!n"; } 这个例子有点复杂。第—个模式(amp..)匹配字符串ample。这意味转整个模式成为字符串ample example,其中带下划线的文本对应于1。因此,模式匹配的是 sample examples。 下面是同样风格更复杂的例子; $string = ' bballball'; $string =~ s"?(b)1(a...)12" $1$2"; 让我们详细地看看这个例子。该例完成匹配,但是原因不是太明显。对这个字符串的匹配有五个步骤: 1)在圆括号中的第一个b匹配字符串的开头,接着将其存放在1和$1中。 2)1于是匹配字符串中的第二个b,因为与b相等,而第二个字符碰巧是b。 3)(a..)匹配字符串all且被存在2和$2中。 4)1匹配下一个b。 5)因为2等于all所以匹配下一个且是最后三个字符(all)。 将他们放到一起就得到正则表达式匹配bballball,或者说是整个字符串。既然$1等于' b',$2等于all,则整个表达式: $string = ' bballball' ; $string =~ s" (b)1(a..)12" $1$2"; (在这个例子中)转换为如下代码: $string =~ s" (b)b(all)ball" ball"; 或者用行话讲,用bballball替换ball。 ’ 正则表达式看起来很像图 9-5 所示。
s" " "中有一些复杂的反向引用。如果理解了最后一个例子。那么读者在理解Perl的正则表达式如何工作方面远远走在了前面。反向引用可能而且确实会变得更糟。 5.嵌套反向引用 嵌套反向引用对于复杂的难以用单一顺序(一个字符串跟在另一个字符串后面)进行匹配的字符串作用明显。例如下面的表达式: m" ((aaa)*)" ; 使用 * 来匹配多次出现的aaa:即匹配",aaa,aaaaaa,aaaaaaaaa。换句话说,Perl匹配一行中有多个3a的模式。但是这个模式不会匹配aa。假定想匹配如下的字符串: $string = ' softly slowly surely subtly'; 那么使用嵌套园括号后下面的正则表达式会匹配: $string = m" ((s...lys*)*)" ; # note nested parens. 在该例中,最外层的圆括号捕获全部字符串:softly slowly surely subtly 。最内层的圆括号捕获字符串的组合,这种组合是以s开头,以ly结尾且ly后跟空格形成的。因此,正则表达式先捕获surely,将其抛开,然后捕获 slowly,将其抛开,然后捕获surely,最后捕获subtly。这里有一个问题,反向引用按什么顺序出现?读者可能在这个问题上很容易迷惑。是外层圆括号先出现呢,还是内层的内括圆号先出现?最简单的解决办法是记住以下三条原则: 1)在表达式中,一个反向引用越在前,它对应的反向引用编号就越小。例如: $var =~ m" (a)(b)"; 该例中,反向引用(a)变为$1,(b)变成了$2。 2)一个反向引用如果它包含范围越广,则它的反向引用编号就越小。例如: $var =~ m" (c(a(b)*)*)"; 该例中,包含全部内容(m "(c(a(b)*)*)" )的反向引用成为$1。有a嵌套在里面的表达式 m"(c(a(b)*)*)"成为$2。在(m"(c(a(b)*)*)" )中带有b的嵌套表达式成为$3。 3)在两个规则冲突的情况下,规则1优先。在语句$var =~ m" (a)(b(c))" 中,(a)成为$1,b(c)成为$2,(c)成为$3。 因而,在这个例子中,(s...lys*)*成为$1,(s...lys*)*成为$2。 注意这里有另一个问题。让我们一起返回到刚开始的复杂的正则表达式: $string = 'softly slowly surely subtly' $string = m"(((s...lys*)*)"; # note nested parens. 这里(s...lys*)*匹配什么呢?它匹配多个字符串;首先是softly,接着是slowly,再接着是surely,最后是subtly。既然(s...lys*)*匹配多个字符串,那么Perl会抛弃掉第一个匹配而使$2成为subtly。 即便有这些规则,嵌套圆括号仍然可能引起混乱。要做的最好事情就是实践。再一次用这些逻辑的不同组合去实现正则表达式,然后把它们交给Perl解释器。这样做可让读者明白反向引用是以什么次序被Perl解释器进行解释的。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |