Perl笔记:07、以正则表达式进行匹…
一章已经简单的介绍了正则表达式的应用,在本章会介绍在Perl中如何应用这些正则表达式。
以m//进行匹配我们已经在上章简单的使用双斜线的方式来编写模式(注意这里的模式实际上指的就是正则表达式),像/fred/。但实际上,这是m//(模式匹配)操作符的简写。就像我们在说明qw//操作符时提到的,可以选择使用任何成对的定界符。所以,我们可以把同样的表达式写成m(fred)、m<fred>、m{fred}或m[fred]。 这里有个简写:如果你选择双斜线作为定界符,那么你可以省略开头的m。所以大部分的模式匹配都会以双斜线来编写。 用/i来进行行大小写无关的匹配使用/i修饰符,可以在进行模式匹配时不区分大小写,使得你能够轻易匹配FRED、fred或Fred:
print
"Would you like to play a game? "
;
chomp ( $_ = <STDIN> ) ; if ( /yes/i ) { # 大小写无关的匹配 print "In that case,I recommend that you go bowling.n" ; } 用/s来匹配任意字符默认情况下,点号(.)无法匹配换行符,这对大多数单行匹配的情况是合适的。但如果字符串中含有换行符,而你希望点号能用来匹配他们,那么/s修饰符可以完成这个任务。它会将模式中的每个点号按字符集[dD]的效果来处理,就是会匹配任意字符(包括换行符)。下面的实例就是一个包含换行符的字符串,来看看:
$_
=
"I saw Barneyndown at the bowling alleynwith fredn last night.n"
;
if ( /Barney.*fred/s ) { print "That string mentions fred after Barney!n" ; } 因为这两个名称并不在同一行,所以如果省略/s修饰符,上述的匹配就会失败。 用/x加入空白说白了这个修饰符就是为了在模式中加入注释使用的,如果一个很复杂的模式不加入注释的话可能时间长了自己写的模式都会不认识了。如下例子:
/
-? # 零个或一个减号 d + # 一个或多个数字 .? # 零个或一个小数点 d * # 零个或多个数字 /x # 字符串结尾 组合选项修饰符如果在一个模式中使用多个修饰符,可将它们连在一起使用,它们之间的先后顺序并不会影响匹配的结果
$_
=
"I saw Barneyndown at the bowling alleynwith fredn last night.n"
;
if ( /Barney.*fred/is ) { print "That string mentions fred after Barney!n" ; } 将同样的模式展开并加上注释后的模样:
$_
=
"I saw Barneyndown at the bowling alleynwith fredn last night.n"
;
if ( m { barney # 小伙子 barney .* # 之间的任何东西 fred # 大嗓门的 fred }six ) { # 同时使用/s 、/i和/x print "That string mentions fred after Barney!n" ; } 锚位 默认情况下,模式匹配的过程开始于待匹配字符串的开头,如果不相符就一直往字符串后面浮动,看其他位置能否匹配。但是加入一些锚位,可以让模式直接匹配字符串的某处。 某些时候,这两个锚位会一起使用,以确保模式可以匹配整个字符串,这个常见的实例是/^s*$/,这个模式可以匹配空白行。 单词锚位 锚位并不局限于字符串的首尾。比如b是单词边界锚位,它匹配任何单词的首尾。因此/bFredb/可以匹配fred,但无法匹配frederick、alfred或manfred mann。这在文字处理器的搜索命令里,通常称为证词搜索模式。 不过,这里所说的单词并不是一般的英文单词,而是由一组w字符构成的字符集,也就是有普通英文字母、数字与下划线组成的单词。b锚位匹配的是一组w字符的开头或结尾。 在下图中,每个此下发会出现灰色下划线,b会匹配的位置则以箭头标识。因为每个单词都会有开头与结尾,所以字符串中的单词边界一定是偶数个。 此处所谓的单词是指一连串的字母、数字与下划线的组合,也即是匹配/w+/模式的字符串。该句子共有5个单词:That、s、a、word以及boundary。word两边的引号并不会改变单词边界。这些单词是由一组w字符组成的。 非单词边界锚位是B,他能匹配所有b不能匹配的位置。因此模式/bsearchB/会匹配searches、searching与searched,但不会匹配search或researching。 绑定操作符默认情况下模式匹配的对象是$_,绑定操作符=~则能让Perl拿右边的模式来匹配左边的字符串,而非匹配$_。例如:
my
$some_other
=
"I dream of betty rubble."
;
if ( $some_other =~ /brub/ ) { print "Aye,there's the rub.n" ; } 绑定操作符虽然看起来有些像赋值操作符,但它表示不要将模式匹配$_而是将模式匹配我左侧的字符串吧! 下面的这个例子,$likes_perl会被赋予一个布尔值,这个结果取决于用户输入的内容。这个程序属于“急功近利”型的,因为判断之后就丢弃了用户的输入。下面的代码的功能是读取输入行,匹配字符串与模式,然后舍弃输入行的内容。没有进一步使用$_,也没有改变它。
print
"Do you like Perl? "
;
my $likes_perl = ( ) =~ /byesb/i ) ; if ( $likes_perl ) { print "You said earlier that you like Perl.so...n" ; # ... 其他操作 } 因为绑定操作符的优先级相当高,也就没有必要用圆括号来括住模式测试表达式。所以下面这一行如同上面的表达式一样,会将匹配结果(而非该行输入的内容)存进变量: 模式串中的内插正则表达式里可以进行双引号形式的内插。这让我们可以很快写出如下类似grep的程序:
#!/usr/bin/perl -w
use strict ; my $what = "zyq" ; while ( <> ) { if ( /^($what)/ ) { print "Yes you are $whatn" ; } } 无论$what的内容时什么,当我们进行模式匹配的时候,该模式都会成为$what的值。这里和/^zyq/是相同的意思,也即是在每行的开头寻找zyq。但是,$what不一定来自字符串直接量,我们可以从@ARGV里的命令行参数来取得:
如果命令行的参数是zyq|wcl,则模式会变成/^(zyq|wcl)/,也就是在每一样的开头寻找zyq或wcl。这个模式中的括号很重要,如果没有它们,模式就会在字符串的开头匹配zyq或者在字符串的任何地方匹配wcl。 注意:请记住,除非while循环的条件表达式中只有整行输入操作符(<STDIN>),否则输入行不会自动存入$_ 捕获变量到目前为止,我们在模式中使用括号的时候只是为了区分不同的模式组。但括号在模式中还有捕捉变量的功能。所谓的捕捉变量实际上指得是,把(圆括号中模式所匹配的)部分字符串暂时记下了的能力,如果有一对以上的圆括号,就会有一次以上的捕捉。每个被捕捉的对象是原本的字符串,而不是模式。 因为捕捉变量存储的都是字符串,所以它们都是标量变量。在Perl里它们的名字类似$1或者$2。模式里的括号有多少对,匹配变量就有多少个。 这些变量可以去除字符串里的某些部分,因此是正则表达式威力强大的重要原因之一:
$_
=
"Hello there,neighbor"
;
if ( /s(w+),/ ) { # 捕获空白符合逗号之间的单词 print "the word was $1n" ; # the word was there } 当然也可以一次捕获多个字符串:
$_
=
"Hello there,neighbor"
;
if ( /(S+) (S+),(S+)/ ) { print "words were $1 $2 $3n" ; } 上面的程序输出的单词时Hello there neighbor,但输出时没有逗号的,因为模式里的逗号放在圆括号的外面,所以第二次捕捉不会有逗号。使用这个技巧,我们可以精确筛选捕获的(和跳过的)数据。 捕获变量的生命周期 捕获变量只应该在匹配成功时使用;否则就会得到之前一次模式匹配的捕获内容。下面的模式匹配失败的例子本来应该输出从$_捕获的某个单词。但是,如果比对失败,他会输出可能遗留在$1里的任何字符串: 这就可以看出,为什么模式一般都要写在if或while的条件表达式中
#!/usr/bin/perl -w
use strict ; my $wilma = "hello the world!" ; if ( $wilma =~ /(w+)/ ) { print "Wilma's word was $1.n" ; } else { print "Wilma doesn't have a word.n" ; } 捕获的内容不能够永久保存,因此像$1之类的匹配变量如果我们在后面的程序中想要使用其里面的内容,那么最好的方法就是将它赋值给其他定义好的变量,这么做也使得程序代码更容易阅读:
不捕获模式既然括号在模式中有分组和捕获的功能,那如果我在一个模式中即想用分组,又想用捕获,但我却不想捕获第一个本意为分组的内容,那又怎么办呢?当然我可以使用$2、$5之类的变量来代替,但若我就想用$1、$2又当如何处理呢?看看下面的例子:
正如上面的例子所示,使用(?:)的形式就可以不捕获这个模式了! 命名捕捉 上面已经讲了可以使用括号的捕捉能力并且在$1、$2这样的变量中存取捕捉的串。但是即使对于较为简单的模式来说,管理这样的数字变量也是比较困难的,更别说大的模式匹配了。所以要想一个便于识别的变量来代替。在Perl 5.10引入了正则表达式命名捕捉的概念。它会将捕捉的记过存入一个特殊的哈希%+,其中的键就是在捕捉时候使用的特殊标签,其中的值则是被捕捉的串。
use
5.010
;
my $names = 'Fred or Barney' ; if ( $names =~ m/((?<name2>w+) (and|or) (?<name1>w+))/ ) { say "I saw $+{name1} and $+{name2}" ; } 自动匹配变量除了上面介绍的捕获变量外还有3个另类的匹配变量。他们是:$&、$`、$' 含义如下: $& 字符串里实际匹配模式的部分自动存入该变量 看看下面的实例:
if
(
"Hello there,neighbor"
=~
/s(w+),/
)
{
print "That was ($`)($&)($').n" ; } 输出结果为:That was (Hello)( there,)(neighbor). 使用自动匹配的弊端 通用量词 模式中的量词代表前置条目的重复次数。目前已经看到的有:*、+和?。在这里我们再介绍一个({})花括号,花括号里指定重复次数的范围。因此: 事实上,我们之前提到的三个量词字符都只是常用的简写而已。星号相当于{0,},加号相当于{1,},问号相当于{0,1}。 =====================本章习题========================= 1、利用模式测试程序,写个模式,使其能够匹配到match这个字符串,你可以把beforematchafter输入到程序里测试,看是否会正确显示匹配到的部分以及前后的部分?
#!/usr/bin/perl -w
use strict ; $_ = "beforematchafter" ; if ( /(match)/i ) { print "$`n$1n$'n" ; } 输出结果为: 2、利用模式测试程序,写个模式,使其能够匹配任何以字母a结尾的单词(以w组成的单词)。此模式是否能够匹配到wilma?是否无法匹配到barney?此模式是否能够匹配到Mrs._Willma_Flintsone?还有wilma&fred呢?把这些测试字符串加到该文本文件里测试程序。
#!/usr/bin/perl -w
use strict ; while ( <> ) { if ( /b(w*a)b/ ) { print "$1 in $_n" ; } } letter.txt
wilma
barney Mrs. Wilma Flintstone wilma&fred 输出结果为: 3、修改上一题的程序,使其定在定位以a结尾的单词后,在将之后的5个字符(如果有那么多的话)捕获至一个独立的内存变量。修改程序输出,把所用到的这两个内存变量都输出来。
#!/usr/bin/perl -w
use strict ; while ( <> ) { if ( /(bw*ab)(.{0,5})/s ) { print "$1 after is $2 in $_n" ; } } 输出结果为: 4、写个新程序,输出其输入中以空白结尾的行。
#!/usr/bin/perl -w
use strict ; while ( <STDIN> ) { if ( /( $)/ ) { print "you input string end is space!n" ; } else { print "no space!n" ; } } 输出结果 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |