正则表达式的基本用法
Perl 有很多其它语言所没有的特性,这其中对正则表达式(regular expression)的强大支持是它最为突出的一个亮点。正则表达式使得 perl 在处理文本时具有非常强大的优势:快速,灵活而且很可靠,甚至可以说,强大文本处理能力,是 perl 在众多语言中最为闪耀的一个特点。 因此学习 perl 的过程,必然也是学习正则表达式的过程,这或许多少给 perl 的学习增加了些少的负担,但好在正则表达式并不是 perl 所独有的, 它是一门使用非常广泛的语言,在很多工具及其它编程语言中都有广泛的支持,比如:grep,awk,sed,vi 等。 它是如此的常见,以致于编程人员在很多场合都无可避免的要与之打交道,因此掌握好正则表达的好处是非常明显的,好在它的语法也很简单,学习起来也不算太难。
在 Perl 或其它一些语言及工具中,正则表达式通常也会叫为“模式"(pattern),正则表达式本质上来说是一个字符串模板,用来确认某个字符串是否符合这个模板的格式,任何一个字符串,要么符合这个模板,要么不符合这个模板。具体到在Perl中,正则表达式是用"/"围起来的,比如:($string =~ /pattern/)。在这里我们暂且称 string 为匹配串,只有当 string 匹配了 ”pattern" 这个串时,这个表达式才是true,反之为false。如: "abc ef ffff" =~ /ef/ 为 true,"abc" =~ /ee/ 为 false。
当然上面的例子只是最简单的情形,如果正则表达式只能做这样单纯的纯字符匹配,那它就不可能这么强大。正则表达式通过一类特殊的字符,我们称为元字符或通配符(meta character)来进行字符的模糊表示,它们在匹配的过程中代表特殊的含义。下面我们就进行简单的介绍。
(1) 点号(.),它用来匹配任意一个单字符(\n 排除在外,后面我默认不再提这个例外)。
所以: "twoon" =~ /tw.on/ 为 true
“twvon" =~ /tw.on/ 也为 true.
点号在正则表达式中是有特殊含义的,有时我们可能也要匹配点号,这时就需要转义一下。
"twoo.n" =~ /twoo\.n/ 为true.
正则表达中,所有其它的通配符也都可以用同样的方式进行转义,表示直接匹配通配符,去除它的特殊含义。
你可以看到\也是一个通配符,如果要匹配它也是同样的道理。
"two\\on" =~ /two\\on/ 为true.
(2) 星号(*) : 星号代表匹配它前面一个字符任意遍(0或任意次),它是一数量词(quantifier),必须跟在其它字符的后面,否则这个表达式不正确。
如: ”twoon" =~ /two*n/ 为true
“twn" =~ /two*n/ 也为true.
"twoon" =~ /*twoon/ 表达式不正确,*必须跟在其它符号后面.
同时星号也可以匹配点号(.),点号代表任意非回车字符,因此, (.*)就代表任意字符任意次。这是一个惯常的用法,如: /twoon.*walks/ 能匹配任意包含"twoon"在前,"walks“在后的字符串。所以(.*)也被称为:any old junk. 匹配任何东西。
(3) 加号(+): 加号是一个与星号(*)类似的通配符,它也是数量词,表示匹配前面的字符一次或多次(至少一次).
它与星号的差别就在这里,星号可以匹配0次,加号则必须一次以上。
如:"twoon" =~ /two+n/ 为true.
"twn" =~ /two+n/ 为false.
(4) 问号(?): 问号也是一个数量词,它代表匹配前一个字符0或1次。
如: "twoon" =~ /twoo?n/ 为true
"twoon” =~ /two?n/ 为false.
"twn" =~ /two?n/ 为true.
(5) 括号(()): 括号用来表示一个组合,前面我说数量词作用在前一个字符上,这个说法事实上不准确,应该说是作用在一个组合上,一个字符是一个组合,但多个字符也可以成为组合。括号就是用来表示一个组合,被括号括起来的就是一个组合。
如: "twoon" =~ /tw(o)*n/ 为true.
"twoon" =~ /tw(oo)*n/ 为true
"twowon" =~ /t(wo)*n/ 为true.
"twoon" =~ /t(wv)*oon/ 为false.
“twoon" =~ /t(wo)+on/为true.
"twon" =~ /t(wo)+on/为false.
括号里可以放置任何字符,也可以放置其它通配符,如 "aaabcc" =~ /(aa+b)?cc/
(6) 引用通配符:反斜杠加上数字是所谓引用通配符(back reference): \1 \2 \3 等,它的作用是引用前面的某个括号元组,如:
"twoonwo" =~ /t(wo)on\1/ 为true
乍看起来,似乎作用不明显,如上例,我们完全可以不用\1,而写成这样: /t(wo)on(wo)/
在上面的例子里,这个质疑是可以理解的。但有时,我们的括号元组可能这样写的: (we...) 因为点号代表任意字符,如果我们后面要作用这个元组,不用引用通配符, 我们根本无法引用,具体看例子:
”weabceeweabc" =~ /(we...)ee\1/ 为 true。
“weabceeweabc" =~ /(we...)ee(we...)/ 为 true
”weabceewecdf" =~ /(we...)ee(we...)/ 也为 true.
从第2,3个例子,我们可以看区别。\1 表示的是与前一个元组完全一样的匹配。而 \1,\2,\3等,则分别表示,从左往右数第几个元组。
“abcdef def abc" =~ /(...)(...) \2\1/ 为 true.
在 perl 中,引用通配符中支持从1~9,写法上很活,你既可直接\1 \2 ...\9这样来写,也可以写成 \g{1] ,\g{2},.....\g{9}。后面一种写法相对复杂些,但有助于perl来理解你想表达的含义。因为反斜杠在程序语言中有特殊的信念,通常表达转义,perl 在遇到反斜杠时,它会去猜你想表达的什么。所以如果你写一个类似: \123这样的东西,它就不知怎么去解析,你是想表达 \1+23,引用后面跟着数字,还是,\12+3,或 \123,转义符后面跟数字是可以表示转义一个8进制数字的。因此这里产生了歧义。perl 5.10 于是引入了 \g{N}这种表述方式来表示引用通配符。N 甚至可以是负数,当是用负数是,它表示一个相对位置。表示从当前位置开始往左数,第N个元组,如:
"twooavvboonn" =~ /tw(oo)a(vv)b\{-2}(nn)/ 为true.
(7) 中括号[]: 中括号用来表示一个字符集合(character set)
字符集合,顾名思义,就是字符的集合,集合的元素放在中括号里,表示每次匹配中其中的一个,如: "twoon” =~ /[tw]woo/ 为 true
有时如果这个集合有很多元素,如26个字母,数字等,一个个地写在中括号里,未免太麻烦太蠢笨,这时可以用连字符(hyphen)来表示一个范围,如:[a-z]表示小写字母的集合,[a-zA-Z]表示大小写字母的集合。
上面的用法用于提供范围来选择,但有时不匹配某个范围也是很常见的匹配需求,这时我们可以在集合的开头放一个脱字符 ^ (caret). 这种写法表示,匹配任何不在该集合中的字符,与上面的用法刚好相反。如:"twoon" =~ /[^two]woon/ 为false
“ewoon" =~ /[^two]woon/ 为true
由上面的用法,可知 ^,- 这两种符号在集合中有特殊含义,如果我们们想在集合中表示这两个字符,就也要转义一下。如:[\^ab\-]
有些字符集合是很常用的,如字母,数字等,perl提供了一些缩写来表示这些常用的集合,如:\d表示一个数字,等价于[0-9],这些特殊字符包括如下 :
\w -- (小写w) 表示字母或数字,等价于 [a-zA-Z0-9]
\W -- (大写W)非字母且非数字,与\w相反
\s -- (小写s)匹配一个空格字符,包括:空格,换行,回车,tab,等价于[ \n\r\t\f]
\S -- (大写S)匹配非空格字符,\s的相反
\d -- 表示10进制数字,等价于 [0-9]
(8) 大括号:{}
大括号的作用是指定重复前面一个字符多少遍:
{N} 重复N遍
{n,m} 重复 n~m 遍
{n,} 至少重复n遍
{,m} 至多重复m遍
示例:"twoon" =~ /two{2}n/ 为true.
(9) ^,& 这两个通配符用来表示在匹配串的头部或尾部匹配。
一般我们写这种: "twoon" =~ /oo/ 正则表达式的时候,匹配是从"twoon"的开始一路匹配下去,如tw != oo,就继续往下匹配,但有时候我们可能只想匹配一下开头或结尾,这时^,&就派上用场了。^用于匹配字符串的开关,&用于匹配字符串的结尾。如: "twoon" =~ "^tw" 为true.
“twoon” =~ “oo" 为true
"twoon" =~ "^oo" 就为false.
”twoon" = "on&" 为true.
"twoon“ = ”oo&" 为false
(10) “或" 通配符: 正则表达式用竖线 | 表示或, (ab | cd) 表示匹配竖线左右的字符组之一,如果左右的字符数超过一个,它必须和括号一起使用。
如: "twoon” =~ /t|ewoon/ 结果为true
"twoon" =~ /(tw|ee)oon/ 结果为true
"twoon" =~ /(ee|gs)oon/ 结果为false
上面简单地介绍了正则表达式中各种通配符的含义及用法,可以看出,正则表达式在语法上还算是简单的,但这并不代表它也只会做简单的事情,事实上,正则表达式也可以写得很复杂,语法的简单或复杂与该语言功能强大与否并没有太多直接的联系。学习正则表达式可以让我们在很多场合下受益,特别是在使用unix类系统下的各种工具时,只是这种语言也和很多其它很多语言一样,学而不用就很容易忘。本文只是简单的介绍了一下它的基本语法,基本写法,要想做到熟能生巧,就需要在日常工作中,多练习,多操作,多留心了。