Perl的正则表达式——入门篇
一.基本概念
正则表达式,在Perl 中通常被称为模式(pattern):某个模板是否匹配某个字符串。由于存在无限的字符串,某个给定的模式将这些字符串分成两类:一类是能匹配的,一类是不能匹配的。这里没有,或者,大概,几乎那样的匹配:要么匹配,要么不匹配。
元字符 meta-characters:
点号(.)是通配符:它可以匹配任何单个的字符,但不包括换行符(“\n”)。
反斜杠(\)是转义字符:用于在本义和转义之间切换。如\.表示匹配’.’自身,而\n表示换行。
量词 quantifier:意指其可以指代多少个前面的项,必须紧跟在某些项的后面
星号(*)表示匹配前一项0次或者多次。因此.*将匹配任意字符任意多数。
加号(+)表示可以匹配前面一项的1次或多次。至少出现1次。
问号(?)表示前面一个项出现一次,或者不出现,即1次,或者0次。
分组:括号(())用来表示分组,表示作为一个小的整体
选择符:竖线(|),在这种用法中通常被读作“或(or)”,意思是匹配左边的或者右边的。
字符类:是方括号[]中的一列字符,可以匹配上括号内出现的任意单个字符。它匹配一个字符,但这个字符可以是列中的任意一个,可以使用连字号(-)来表示某个范围的字母。
字符类的补集:字符类前使用符号^表示取此字符类的补集,表示匹配除此之外的字符。
字符类的简写:
\d 匹配任何数字的类,== [0-9]
\w 被称作“word’字符:== [A-Za-z0-9_] 注意下划线也算在内!
\s 匹配空白字符(whitespace) == [\f\t\n\r ],依次是格式符;制表符,换行符,回车,以及空格。
简写形式也有补集:可以使用[^\d], [^\w], 和[^\s],其含义分别是,非数字的字符,非word(记住我们对word 的定义)的字符,和非空白的字符。也可以使用它们对应的大写形式:\D, \W, \S 来完成。
二.使用m//匹配
Perl语言过于灵活,几乎可以使用任何成对的分隔符表示模式 pattern。如m//, m(), m[], m{}, m!!等等。但是如果使用正斜杠/,则可以省去m,使用起来通常最为方便。
模式后面可以增加修饰符,来改变默认的行为:
/i 表示进行大小写无关的模式匹配
/s表示使(.)能够匹配任意字符(默认情况下(.)不匹配换行符)。这样很容易解决跨行匹配问题。
/x允许在模式中加入任何数量的空白,以方便阅读
锚定(anchors):默认情况下,如果模式在字符串开头没能匹配上,它会顺着字符串下去,直到匹配上为止。锚定则可以要求模式在特定的位置进行匹配。^ 表示在字符串的开头进行匹配,$则表示在结尾。如/^\s*$/将匹配一个空行。
词界锚定:\b,是针对单词使用的,仅仅针对字符串的两头。两头都有时,即全字匹配(match whole words only)。
非词界锚定:\B。它将在任何非\b 匹配的点上进行匹配。
绑定操作符=~:对$_进行匹配只是默认的行为,使用绑定操作符(=~)将告诉Perl 将右边的模式在左边的字符串上进行匹配,而非对$_匹配。
匹配变量:
我们曾经在模式中使用过括号,使用括号是由于它可以将模式的某一部分组合起来。同时括号也会引起正则表达式分配新的内存块。这些内存含有括号中的模式所匹配的字符串。如果有不止一对括号,那就不止一块内存块。每一个内存块内有一段字符串,而非模式的一部分。
由于这些变量含有字符串,那它们是标量变量;在Perl 中,它们具有像$1, $2 这样的名字。变量个数同模式中括号对数的个数是相同的。如$4是指第四对括号所匹配的字符串。
这些匹配变量(match variables)是组成正则表达式强大功能的重要部分,它允许取出相应的字符串。
这些匹配变量的值会保持不变,直到下一个模式成功匹配为止。也就是说,一个没有匹配成功的模式将不会改变内存中相应的值,但一个匹配上的模式将重写此内存。这明确的告诉你,不要随意的使用这些变量,除非明确知道它们匹配正确;否则,你可能得到上个模式匹配的结果。
自动匹配变量:还有三个匹配变量,无论这些模式是否有括号。
三个变量名为: $&, $`, $'。其中$&表示整个被匹配的部分,$`中含有正则表达式引擎在匹配成功前所找到的变量,而$'为此模式还没有匹配的剩余部分。如果将这三个变量放在一起,你将得到原始字符串。(不推荐使用这三个变量,运行速度稍慢)
一般的量词:如果*, +, ?这三个数量词仍不能满足你的要求,那可以使用花括号({}),花括号中有2 个数字,由逗号隔开,表示前面一项允许重复的次数。模式/a{5,15}/将匹配5 个到15 个a 中的任意一个(包括5,和15)。如果省略掉第二个数字(逗号留下),表示没有上限了。如果逗号也被省略了,那将匹配确定的次数。
优先级:正则表达式的优先级表更简单,只有4 个级别:
1.在此优先级表的最顶端是括号:(()),在分组和引用内存值的时候使用。括号内部的任何部分比括号外的部分结合更紧密。
2.第二级是数量词。这里有星号(*), 加号(+),问号(?)以及由花括号表示的数量词,如{5,15}, {3, }, {5}等。它们通常和前一项元素结合。
3.第三级的是锚定和序列(sequence)。锚定包括(^) 表明字符串的开头,($)表明结尾,(\b)词界符,(\B)非词界符。序列(一个元素紧接着一个元素)实际上是一种操作,虽然它没有使用元字符。这段话的含义是一个单词中的字母结合更紧密,就像锚定紧贴字母一样。
4.优先级最低的是竖线(|),表示或。由于其优先级最低,它通常将模式划分成几个部分。它在优先级最底端是因为我们希望像/fred|barney/里面的字母比或(|)结合更紧密。如果或(|)的优先级比序列的优先级更高,那么,上述模式的含义是匹配fre,接着是d或者b, 然后是arney。因此,或(|)的优先级最低,字母序列的优先级要高些。
三.使用s///替换
如果将m// 这个模式匹配看作同文字处理器的“查询(search)”类似的功能,那Perl 中s ///操作的则类似于“查询并替换(search and replace)”。它将替换变量中模式所匹配上的部分,当然,s///只能修改被称为左值(lvalue)的数据。如果没有匹配上,则什么也不会发生,此变量也不会有任何更改。
全局替换/g:s///默认只进行一次替换,无论是否还有地方还能匹配上。修饰符/g 要求s///将不相重叠的所有匹配上的部分都进行替换。全局替换的一个常用地方是将多个空格用单个空格替换掉:
s/^\s+//; #将开头的空白去掉
s/\s+$//; #将结尾的空白去掉
大小写替换:
\U 表示把紧接着的都替换为大写字母;\L 表示替换为小写。
$_ =“I saw Barney with Fred.”;
s/(fred|barney)/\U$1/gi; #$_现在是“I saw BARNEY withFRED.”
s/(fred|barney)/\L$1/gi; #$_现在是“I saw barney withfred.”
默认时,会影响到剩余的(替换的)字符串。可以使用\E 来改变这种影响:
s/(\w+) with (\w+)/\U$2\E with $1/I; #$1 现在是“I saw FRED with barney.”
使用小写形式时(\l 和\u),只作用于下一个字符:
s/ (fred|barney)/\u$1/ig; #$_现在是“I saw FRED with Barney.”
也可以同时使用它们。如使用\u 和\L 表示“第一个字母大写,其它字母均小写”。
s/(fred|barney)/\u\L$1/ig; #$_现在为“I saw Fred withBarney.”
四.非贪婪的量词
前面已经出现过的四个数量词均是贪婪的。这就是说它们将最大限度的匹配,而非在匹配成功时即立刻返回。下面是一个例子:假设在fred and barney went bowling last night 上使用/fred.+barney/进行匹配。我们知道正则表达式将匹配上,下面我们具体的讲解这一个过程。
首先,子模式fred 将匹配其对应的字符串。模式的下一部分是.+,它将匹配除了换行符之外的任意字符,次数大于等于一。但,由于加号(+)是贪婪的;它将尽可能的进行匹配。因此,它将匹配剩余的所有字符串,包括night。这可能让你惊奇,但故事还没结束。现在对banrey 进行匹配,但不能成功,因为已经到了字符串的结尾处。由于.+在少一个字符的情况下仍能匹配成功,因此它退回字符串最后一个字母t。(它虽是贪婪的,但更希望整个模式能匹配成功。)子模式barney 又尝试匹配,结果仍是不行。因此.+再退回字母h,又进行匹配。一个字符接一个字符,.+退回其匹配的字符,直到其退回了字符串barney。最后,子模式banrey 被匹配上了,现在整个模式都匹配上了。
正则表达式引擎有大量的像上面那样的回退(backtracking)操作,尝试每一种可能,直到成功或者根本不能匹配为止。如本例所显示的那样,这些操作引起了大量的回退操作,因为这些数量词匹配了太多的字符串,正则表达式引擎强迫它们返回一些。
因此对于每一个贪婪的数量词,需要一种非贪婪的方法。不是使用加号(+),而是使用非贪婪的数量词+?,它将匹配一次或多次(加号的意思),但其匹配尽可能少的次数,而非尽可能多的次数。现在我们来看看模式为/fred.+?barney/时的过程。
首先,fred 将被匹配上。接着,模式的下一部分是.+?,它匹配的字符个数不大于1,因此匹配fred 后面的空格。下一个子模式是banrey,它在这里不能被匹配(因为现在的位置是and barney… 的开头)。.+?再匹配a,剩下的模式继续进行匹配。又一次,barney 不能匹配上,因此.+?再匹配n,依次类推。当.+?匹配了这5 个字符后,barney 可以被匹配上了,现在模式匹配成功。
这里也存在一些回退操作,但由于引擎只需回退,并只尝试几次,其在速度上会有很大提高。但,这种提高依赖于banrey在fred 的附近能被找到。如果数据中fred 在字符串的开头,而barney 在结尾处,则贪婪数量词方法的速度更快。因此,正则表达式的速度依赖于具体的数据。
由于加号的非贪婪类型是+?,星号的为*?,你可能已经意识到剩下的两种数量词其对应的类型也是类似的。花括号的非贪婪类型看起来一样,只是在闭花括号后有一个问号,如{5,10}?或者{8,}?。甚至问号数量词也有非贪婪类型:??。它匹配一次或者0 次,但倾向于匹配0 次。
注:参考《Perl 语言入门(第四版)》