Chinaunix首页 | 论坛 | 博客
  • 博客访问: 570008
  • 博文数量: 201
  • 博客积分: 7734
  • 博客等级: 少将
  • 技术积分: 1994
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-09 19:18
文章分类

全部博文(201)

文章存档

2011年(28)

2010年(173)

分类:

2010-08-31 14:35:09

第七章:
漫游正则表达式王国
什么是正则表达式?
正则表达式,在Perl中常常叫做模式,是一个匹配(或不匹配)某字符的模板。
使用简易模式:
 

#!/usr/bin/perl -w

$_ = "yabba dabba doo";
if (/abba/) {
print "It matched!\n";
} else {
print "It is not matched!\n";
}


关于元字符:
点号(.)是任何单字符的通配符,当然换行("\n")要除外。因此,betty将会被/bet.y/这个模式匹配,而betsy、bet=y、bet.y,或是任何前三个字符为bet、中间接任何一个字符(换行符除外)、后面为y的字符串,也都会被/bet.y/匹配。

要是希望只能匹配句点号本身,只需在前面加上反斜线就好了。例如:/3\.14159/这个模式里面就没有通配符。如果要得出真正的反斜线,用两个反斜线表示。

简易的量词:

*用来匹配重复多次或零次的内容。
/fred\t*barney/能匹配fred和barney之间有任意多个制表符的串。也可以是零个制表符。

如果除了制表符外还想匹配其它字符就用(.*),它会区配任意字符无限多次。也就是说,不管fred与barney之间夹着什么东西。
都会匹配/fred.*barney/。.*经常戏称捡破烂模式。因为它能通吃所有的字符串。

星号是一种量词,+号也是一个量词。加号会匹配前一个条目一次以上。
/fred +barney/会匹配fred与barney之间用空格隔开而且只用空格隔开的字符串(空格不是元字符)。它不会匹配fredbarney。
因为加号表示两个名称之间必须有一个以上的空格。

第三个量词与星号及加号类似,但是限制更严格。也就是问号(?),表示前一个条目是可有可无的。就是说,它的前一个条目可以出现一次,或是不出现。问号指定了前一个条目出现的次数。可以更为:“刚才所说的,有还是没有都行”。

模式分组:
/(fred)+/ 会匹配像fredfredfredfred这种字符串。
/(fred)*/ 会匹配像Hello, world这样的字符串。

圆括号同时也使得部分字符串重新引用成为可能。我们可以用反向引用圆括号中(的模式所)匹配的文字。\1、\2这样的写法就是
在使用反向引用。而反斜线后面的数字和括号的组号匹配。

#!/usr/bin/perl -w
$_ = "abba";
if (/(.)\1/) { #same 'bb'
  print "It matched same character next to itself!\n";
}
(.)\1表明需要匹配连续出现的两个同样的字符。(.)会首先匹配a,但是在匹配反向引用的时候就会发现下一个字符不是a,导致匹配
失败。Perl会跳过这个字符,用(.)来匹配下一个字符b,在匹配反向引用的时个会发现下一个字符也是b,这样就构成了成功的匹配。

反向引用不必总是附在相应的括号后面。下面的模式会匹配y后面的4个连续的非回车字符,并且用\1在d字符之后重复这4个字符:

 

#!/usr/bin/perl -w

$_ = "yabba dabba doo";
if (/y(....) d\1/) {
  print "It matched the same after y and d!\n";
}

也可以用多个括号来分成多组,每组都可以有自己的反向引用。
$_ = "yabba dabba doo";
if (/y(.)(.)\2\1/) {    
#same 'abba'

  print "It matched the same after y and d !\n";
}

$_ = "yabba dabba doo";
if (/y((.)(.)\3\2) d\1/) {
  print "It matched!\n";
}

Perl 5.10有一种新的反向引用写法。不再只是简单地用反斜线和组号,而是用了g\{N}这种写法。其中N是想要反向引用的组号。
以往在数字模式中使用反向引用,就会觉得非常麻烦。下面的例子我们要用\1来重复刚刚括号匹配的字符,然后是紧接着的原始串
11。如下:

$_ = "aa11bb";
if (/(.)\111/) P
  print "It matched!\n";
}


通过使用\g{1}就能排除前面的模式中的二义性:

#!/usr/bin/perl -w

use 5.010;
$_ = "aa11bb";
if (/(.)\g{1}11/) {
  print "It matched!\n";
}


用\g{N}写法的额外好处是,我们甚至可以用负数,相比绝对位置的反向引用,相对反向引用会更加有趣。

#!/usr/bin/perl -w

use 5.010;
$_ = "aa11bb";
if (/(.)\g{-1}11/) {
  print "It matched!\n";
}


这样若要在模式中加入更多的内容,就不必总是修改反向引用了,因为要加入另外一组括号,就会导致大多数绝对反向引用失效,面相对反向引用则不会,因为它使用的是相对于自己的位置,而不是绝对编号。

#!/usr/bin/perl -w

use 5.010;
$_ = "aa11bb";
if (/(.)(.)\g{-1}11/) {
  print "It matched!\n";
}

择一匹配:
竖线(|)通常可以读成“或”,意思是左边匹配或者右边匹配都行。也就是说如果左边的模式匹配失败了,还可以用右边的再试试。
因此/fred|barney|betty/能匹配任何含有fred或者barney或者betty的字符串。

/fred( |\t)+barney/  匹配fred和barney之间空格、制表符或两者组合出现一次以上的字符串。加号表示重复一次或更多。每次
只要有重复,( |\t)就可能匹配空格或制表符。在这两个名字之间至少要有一个空格或制表符。

/fred( +|\t+)barney/    中间的分隔符就是一定得全是空格或全是制表符。

/fred (and|or) barney/ == /fred and barney|fred or barney/

字符集:
字符集是指一串可能出现的字符集合,通过写在方括号([])内来表示。它只匹配单个字符,但可以是字符集里列出的任何一个。

例如:字符集[abcxyz]会匹配这7个字符中的任何一个。为了方便起见,可以用连字符(-),这样之前的字符集也可以写成[a-cw-z]。这样就非常方便。

定义字符集时可以使用字符简写,类似双引号内的转义序列。因此字符集[\000-\177]将会匹配任何7位的ASCII字符。

$_ = "The HAL-9000 requires authorization to continue.";
if (/HAL-[0-9]+/) {
  print "The string mentions some model of HAL computer.\n";
}


有时候指定字符集范围以外的字符会比指定字符集内的字符更易。可以在字符集内部开头的地方加上脱字符(^),表示这些字符除外。也就是说,[^def]会匹配这三个字符以外的任何字符。

[^n\-z]  匹配n、连字符与z以外的任何字符。

/HAL-[0-9]+/里的第一个连字符则不需要反斜线,因为字符集括号以外的连字符没有特殊意义。

字符集简写:

代表任意数字的字符集[0-9]可以被简写成\d。上面可以写成/HAL-\d+/。

\w表示单词字符:[A-Za-z0-9_],\w并不会匹配一个单词,它只会匹配单词字符集里的一个字符。虽然如此,只要加修饰符就行了。/fred \w+ barney/这样的模式,会匹配fred、一个空格、一个单词再接一个空格与barney。

更灵活的匹配空白:\s它相当于[\f\t\n\r]换页、制表、换行、回车这些都是专门用来移动打印位置的字符,它们不消耗墨水。
\s只会匹配字符集里的某个字符,因此,比较常见的做法是使用\s*来匹配任意数目的空白(也包括零个空白),或用\s+来匹配一个以上的空白符(事实上,不带量词使用\s是很少见的)。

Perl 5.10 增加了\h匹配横向空白,就是一个包含制表符和空格的字符集[\t ]。\v这个用来匹配纵向的空格,其实也就是一个包含[\f\n\r]的快捷方式。\R简写能匹配任何类型的断行。

[^\d] == \D
[^\w] == \W
[^\s] == \S

这些简写既可以作为模式里独立的字符集,也可以作为方括号里字符集的一部分。也就是说,/[\dA-Fa-f]+/可以用来匹配十六进制。
[\d\D]表示任何数字或非数字,也就是说,它会匹配任何字符!这是匹配任意字符(包括换行符)的常见做法。(而点则匹配换行符以外的所有字符)

 

习题:
1、
#!/usr/bin/perl -w

$a = <>;
foreach ($a) {
if (/fred/) {
print "It is matched!\n";
}
}
===================================
#!/usr/bin/perl -w

while (<>) {
  if (/fred/) {
    print;
    }
}

/[fF]red/ /(f|F)red/ /fred|Fred/
=====================================
2、
/\./    /[.]/

======================================
3、
/A-Z][a-z]+/

======================================
4、
/(\S)\1/

======================================
5、
while (<>) {
  if (/wilma/) {
    if (/fred/) {
     print;
    }
  }
}
========================================
while (<>) {
  if (/wilma.*fred|fred.*wilma/) {
  print;
  }
}


阅读(1004) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~