第七章正则表达式

第七章正则表达式
Perl 有许多区别于其它语言的特性。在所有这些特性中,最重要的一条是对正则表达式的支持。它允许你方便,快捷的处
理字符串相关的问题。
但是获得这种能力是需要付出代价的。正则表达式(regular expressions)是一种特殊语言写成的程序,内嵌于Perl 之中(是的,
你要学习一门新的程序语言◆。庆幸的是,它非常简单。)。从本章开始,我们将进入正则表达式的世界,现在你可以暂时
忘掉Perl。下一章中,我们将介绍如何在Perl 中使用正则表达式。
◆ 一些人可能争论说正则表达式不是完备的程序语言。我们不准备在这里争论这个问题。
正则表达式不仅仅是Perl 的一部分;在sed, awk, procmail, grep, 以及许多程序员文本编辑器(programmer’s text editor)如vi,
emacs,还有一些更深奥的程序中都有它的踪影。注意观察,你可以发现许多工具都支持正则表达式,如Web 上的搜索引
擎(通常由Perl 书写),email 客户端,等等。不幸的消息是,不同的正则表达式的实现中,语法会有些微的不同,因此需
要学习其不同的地方,例如需要还是不需要反斜线(\)。
7.1 什么是正则表达式?
正则表达式,在Perl 中通常被称为模式(pattern):某个模板是否匹配某个字符串◆。由于存在无限的字符串,某个给定的模
式将这些字符串分成两类:一类是能匹配的,一类是不能匹配的。这里没有,或者,大概,几乎那样的匹配:要么匹配,
要么不匹配。
◆某些学究可能认为这个定义不够严格。他们可能会说Perl 的模式根本不是真正意义上的正则表达式。如果你想知道更多的关于正则表达
式的信息可以参看Jeffrey Friedl(O’Reily)的书籍《掌握正则表达式》(Mastering Regular Expessions)。
一个模式可以匹配多个字符串:1个,2个,3个,上百个,或者无限个。也可能匹配除了1个,多个,或者无限个字符
串之外的所有字符串◆。我们将正则表达式看作一种由简单语言实现的程序,这种语言只有一个任务:查找某个字符串,
返回“匹配上(it matches)”或者“不匹配(it doesnot match)”◆。这就是它完成的所有工作。
◆将学习到,你可以让某个模式永远匹配或者永不匹配。在极少情况下,这也是有用的,但通常是错误。
◆程序也返回一些信息给Perl。其中的一种信息是“正则表达式内存(regular expressions memories)”,这将在后面会学习到。
你可能在使用Unix 的grep 命令时,见过正则表达式,它会输出匹配上模式的行。例如,如果想查看某个给定文件中是否
某行提到过flint,同时同一行后面提到stone,那你可以如下的使用grep 命令:
$grep‘flint.*stone’chapter*.txt
chapter3.txt:a piece of flint, a stone which may be used to start a fire by striking
Perl 语言入门(第四版)
blei@163.com 93 / 201 9/21/2006
chapter3.txt:found obsidian, flint, granite, and small stones of basaltic rock, which
chapter9.txt:a flintlock rifle in poor condition. The sandstone mantle held several
不要将正则表达式和shell 中的文件名匹配模式,globs 混淆了。通常glob 是指,在Unix shell 下输入*.pm 将匹配所有结尾
为.pm 的文件名。上一个例子使用的glob 为chapter*.txt。(你可能已经注意到我们将模式括起来了,来防止shell 将其作为
glob 来处理)虽然glob 和正则表达式有许多相同的符号,但其作用并不相同◆。第十二章中将介绍globs。
◆globs 有时也被称作模式。但严重的问题是,某些面向初级用户的书籍(可能是菜鸟写得)将globs 叫做“正则表达式”,这绝对是错误
的。
7.2 使用简单的模式
要匹配某个模式(正则表达式)和$_的关系,可以将模式放在正斜线(//)之间,如下:
$_ =“yabba dabba doo”;
if(/abba/){
print “It matched!\n”;
}
表达式/abba/将在$_寻找这四个字母。如果找到,则返回true,在本例中,它出现了不止一次,但结果没什么不同。总之,
如果找到了,则匹配上;如果没找到,则没匹配上。
由于模式匹配通常返回true 或false,因此经常用在if 或while 的条件表达式部分。
所有在双引号中的转义字符在模式中均有效,因此你可以使用/coke\tsprite/来匹配11 个字符的字符串coke, tab(制表符),
sprite。
7.2.1 元字符
如果模式只能匹配字面上的字符串,则其用处不会太大。这也是引入特殊字符的原因,它们被叫做元字符(metacharacters),
在正则表达式中具有特殊的含义。
例如,点(.)是通配符,它可以匹配任何单个的字符,但不包括换行符(“\n”)。因此,模式/bet.y/将匹配betty。同时也匹配betsy,
bet=y, bet.y,或者说任意字符串后接bet,然后是任意的单个字符(不包括换行符),后接y。它不会匹配bety,betsey,因为
t 和y 之间不是一个字符。点(.)只匹配一个字符。
如果想匹配句号(英语中句号就是一个点:译者注),可以使用点(.)。但由于点(.)可以匹配任意的单个字符(除换行符外),
则其结果比你希望的要多。如果只希望点(.)匹配句号,可以使用反斜线。这条规则对Perl 正则表达式中所有元字符均有效:
元字符前使用反斜线将使它变成普通的字符。如,模式/3\.14159/中的点(.)即不是通配符。
反斜线是第二个元字符。如果需要真正的反斜线,需要重复使用两个反斜线,这和Perl 中其它情况下是一样的。
Perl 语言入门(第四版)
blei@163.com 94 / 201 9/21/2006
7.2.2 简单的量词
通常,需要模式中某些串是可以重复的。星号(*)表示匹配前一项0次或者多次。因此,/fred\t*barney/将匹配上fred 和barney
之间有任意个制表位(tab)的字符串。它可以匹配“fred\tbarney”,其间有一个tab;匹配“fred\t\tbarney”,其间有两个制表位;
“fred\t\t\tbarney”其间有三个制表位;“fredbarney”,其间什么也没有。这是由于星号(*)是指“0 个或者多个”,因此其间可以
是任意个制表符,但不能是其它的字符。可以这样看待星号(*):“前面的东西,重复任意次数,包括0 次”(因为*号在数
学上是乘法运算符)。
如果希望包括不同的字符,怎么办呢?点(.)可以匹配任何单字符◆,因此.*将匹配任意字符任意多数。这就是说模式
/fred.*barney/将匹配fred,和barney 之间有任意多个任意字符(不含换行符)的字符串。任意行如果前面有fred,后面有barney,
其间为任意字符(字符串)都将匹配上。我们将.*叫做“任意字符串匹配模式”,因为任意的字符串均能被匹配上(不包括
换行符)。
◆除了换行符。以后我们将不再提醒你,因为你已经知道了。许多时候你不用担心这个问题,因为通常你的字符串中并不含有换行符。但
不应该把这个细节忘掉,因为说不定某人就在你的字符串中加入了换行符,因此应当记住点(.)不会匹配换行符。
星号的正是叫法是数量词(quantifier),意指其可以指代多个前面的项。它不是唯一的数量词,加(+)也是。加(+)的意思是可
以匹配前面一项的一个或多个:/fred +barney/意思是fred 和barney 之间由空格分开,且只能是空格。(空格不是元字符)。
它不会匹配fredbarney,因为加(+)意指一个或多个,因此至少是一个。可以这样看待加(+):“最后一项,(可选的)至少还
有一项。”
还有第三个数量词,其限制性更强。它是问号(?),其含义是前面一个项出现一次,或者不出现。也就是说,前面这个项出
现1 次或者0 次,此外不会有其它情况。因此,/barm-?bamm/只匹配:bamm-bamm 或bammbamm。这很容易记住:“前面
的这个项,出现?或者不出现?”
这三个数量词必须紧跟在某些项的后面,因为它是指前面项重复的次数。
7.2.3 模式中的分组
括号也是元字符。在数学中,括号(())用来表示分组。例如,模式/fred+/能匹配上如fredddddddd,这样的字符串,但这种字
符串在实际中没有什么用途。模式/(fred)+/能匹配上像fredfredfred 这样的字符串,这更可能是你所希望的。那么模式/(fred)*/
呢?它将匹配上像hello,world 这样的字符串◆。
◆星号(*)意指匹配上0 次或者多次fred。当为0 时,那什么字符串都能被匹配上。这个模式能匹配上任何字符串,甚至是空串。
7.2.4 选择符
竖线(|),在这种用法中通常被读作“或(or)”,意思是匹配左边的或者右边的。如果竖线左边没有匹配上,则匹配右边。因
此,/fred|barney|betty/将匹配出现过fred,或者barney,或者betty 的字符串。
Perl 语言入门(第四版)
blei@163.com 95 / 201 9/21/2006
现在你可以书写像/fred( |\t)+barney/这样的模式,它将匹配fred,barney 以及中间由空格,制表符(tab),或者二者混合所组
成的字符串。加(+)是指重复1 次或多次;每重复一次,( |\t)则有可能匹配一个空格,或者一个制表符◆。但fred 和barney
之间这些字符中(空格,制表符)的其中之一必须出现一次。
◆如果使用字符类(character class)进行这种匹配将更有效,这在本章后面会介绍。
如果希望fred 和barney 之间的字符是一样的,可以将模式写成/fred( +|\t+)barney/。在本例中,分隔符必须全是空格或者全
是制表符。
模式/fred (and|or) barney/能匹配如下两种字符串:fred and barney, fred or barney◆。也可以将模式写成: /fred and barney|fred
or barney/,但这样书写的字符更多。并且其效率也更低,这依赖于正则表达式引擎中所使用的优化方法。
◆单词and 和or 在正则表达式中不是操作符!它们在正则表达式中就是其本来的含义:单词and,or。
7.3 字符类
字符类,是方括号[]中的一列字符,可以匹配上括号内出现的任意单个字符。它匹配一个字符,但这个字符可以是列中的
任意一个。
例如,字符类[abcwxyz]可以匹配上括号内七个字母中的任意一个。为了方便,我们可以使用连字号(-)来表示某个范围的字
母,因此上例也可以写做[a-cw-z]。上面例子省略的字符不多,但像[a-zA-Z]将非常方便,利用它不需要输入52 个字符◆。
你可以使用和双引号相同的字符简写方法,例如类[\000-\177]可以匹配上任意的七比特的ASCII 字符。◆。当然,字符类
只是模式的一部分,单独的字符类在Perl 中没什么实际的意义。例如,你可能见到如下的代码:
◆注意这52 个字母不包括Å, É, Î, Ø, 和Ü。如果允许处理Unicode,则上述字符的范围将自动的变化,来做正确的工作。
◆这里,使用的是ASCII 而非EBCDIC。
$_ = “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]+/中第一个连接符(-)前不需要反斜线,因为此时的连接符不
会被理解为有特殊的含义。)
7.3.1 字符类的简写
有一些字符类出现的非常频繁,因此提供了其简写形式。例如,任何数字的类,[0-9],可以被简写为:\d。因此,HAL 这
Perl 语言入门(第四版)
blei@163.com 96 / 201 9/21/2006
个例子可以被写作/HAL-\d+/。
\w 被称作“word’字符:[A-Za-z0-9_]。如果你的“words”由通常的字母,数字,下划线组成,那你将非常喜欢它。通常认
为“word”由字母,连接符(-),撇号(')◆组成,我们希望能改变这种定义◆。因此使用它,请记住我们对“word”的定义,
字母,数字,下划线组成。
◆至少,在英语中是这样。在其它语言中,其words 由不同的符号组成。查看perllocale 的帮助手册了解更多的信息。
◆当查看ASCII 编码的英语文本时,我们遇到单引号和撇号(')是相同字符的问题,因此很难说cat’是cat 和一个撇号( '),还是cat 后接单引
号。这可能是计算机还不能接管世界的一个原因。
当然,\w 不能匹配单词,而只能匹配单个字符。为了匹配整个单词,需要后接加号。模式/fred \w+ barney/将匹配fred,空
格,一个“单词(word)”,然后是空格和barney。因此,如果fred 和barney 之间有一个单词◆,由单个空格分隔开,它将
能匹配上。
◆我们将停止在word 上加引号;现在你已经知道其是由字母-数字-下划线组成的。
你可能已经注意到在前一例中,如果能更加灵活的匹配空白将很方便。\s 对于匹配空白(whitespace)将非常方便。它等价
于[\f\t\n\r ],其含5 个空白字符:格式符(form-feed);制表符(tab),换行符,回车,以及空格符。同其它简写符号一样,
\s 匹配此类中的单个字符,如果使用\s*将匹配任何个数的空白(包括没有),或者\s+匹配一个以上的空白(事实上,很少
见到单独使用\s,而不使用任何的数量词(*, +))。由于这些空白符看起来类似,因此可以使用这种简写形式,将它们统一处
理。
7.3.2 简写形式的补集
某些时候,你可能希望得到这三种简写形式的补集。如果那样的话,你可以使用[^\d], [^\w], 和[^\s],其含义分别是,非数
字的字符,非word(记住我们对word 的定义)的字符,和非空白的字符。也可以使用它们对应的大写形式:\D, \W, \S 来
完成。它们将匹配它们对应的小写形式不能匹配上的字符。
这些简写形式可以在字符类中使用,或者在大的字符类中的中括号里面使用。也就是说你可以使用/[\dA-Fa-f]+/来匹配十六
进制(底为16)的数字,它将ABCDEF(或者其小写形式)作为附加的数字(11 到15)。
另一个类字符[\d\D],它的意思是任何数字,和任何非数字,则意指任何字符。这是匹配所有字符的一种通用方法,甚至包
括换行符,而点(.)匹配除换行符以外的任何字符。而[^\d\D]则完全没用,因为它匹配既非数字也非非数字的字符,那什么
也不是。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值