正则表达式入门(3)——重复匹配

这个问题是:如何匹配连续多个重复出现的字符或字符集合?例如,如何匹配一个电子邮件的格式?

text@text.text

电子邮件的格式包含了三段,但我们不知道每一段的长度。

1.匹配一个或多个字符

在字符或字符集合后面跟一个“+”(加号),表示匹配1个或多个该字符或字符集合。例如,[0-9]+就表示一串任意长度的数字。

回到电子邮件的例子。电子邮件中的三个字段,其组成字符应该是\w类的,所以可以表示为:

\w+@\w+\.\w+

注意w是小写的,大写W表示“非”。

但是这还是有问题,因为有些电子邮件的地址中包含了“.”点字符(起这种邮件名真是蛋疼),那么\w是不包含点字符的。比如:

michael.jackson@moon.com

这样的地址,上面的正则只能匹配成jackson@moon.com了。

但是,还有些电子邮件地址有多级域名,例如:

tom.jerry@sina.com.cn

所以,要用[\w.]才能描述这种情况了。重新修改一下电子邮件的正则,应该是:

[\w.]+@[\w.]+\.\w+

已经开始有点看不懂了……这里要特别注意的是,字符集合中的“.”或者“+”等元字符,与字符集合外的规则不同,他们只表示自己,不需要转义。也可以说:

字符集合内(即方括号[]内)的语法与字符集合外是不同的,务必注意。

还有一点,+加号表示1个或以上,而不包括0个。如果要包括可有可无的情况,那么就用*(星号)替换+(加号)。

2.匹配零个或多个字符

考虑有这么一句话:

Hello, my email address is .ben@red.net.

如果用前面的正则,会匹配出这个结果:

.ben@red.net

显然前面多打了个“.”,而点字符在邮件地址中可以出现,却不能出现在开头,那就是不合法的。这个时候,我们要确保第一个字符是\w类才行。

再次修改邮件的正则格式:

\w+[\w.]*@[\w.]+\.\w

现在ok了,会自动把前面那个“.”给去掉了。注意[\w.]*表示可有可无。如果仍然用[\w.]+的话,则邮件名的@前面至少要有两个字符才能匹配了。

3.匹配零个或一个字符

问号(?)与点符号(.)的区别是:问号也是匹配任意单个字符,但可以有,也可以没有,而点符号隐含的意思是“必须有”。

例如,要匹配http和https,应该写:

https?

它表示http后面的s可有可无,都能匹配。

[\r]?与\r?的区别是什么?

这两种写法的匹配结果是一样的,当然带[]会更容易读一些。但实际的语义一样吗?这个问题留待以后研究吧。

4.匹配精确重复数

+和*匹配的字符数是任意长度,但如果要精确限定重复字符的个数,需要用{}元字符。再例如颜色值的表达式,是一个#后面跟6个十六进制字符。重复写6遍[A-Fa-f0-9]或者[[:xdigit:]]都可以,但还有简单的写法:

#[[:xdigit:]]{6}

就可以了。

5.重复匹配数量的区间

如果要匹配一堆长度在3位到8位之间的数字,该怎么写呢?

[0-9]{3,8}

就这么写,很简单,只包含长度在3位到8位之间的数字串,会把22或者987654321等长度不符合要求的字符串给过滤掉。

由此还可以推测出,如果要限制最小重复次数,或者最大重复次数,只要去掉逗号左右其中一个数字就行了。例如:

[a-z]{5,}    //5位以上长度的连续小写字母串
[A-Za-z]{,30}    //30位以下长度的连续字母串

6.“贪婪型”元字符和“懒惰型”元字符

比如有这么一句话:

living in <B>AK</B> and <B>HI</B>.

这是常见的html标记。如果要提取AK和HI,也许你会写成:

<B>.*</B>

但是这会悲剧的提取出下面这句结果:

<B>AH</B> and <B>HI</B>

中间的\和\都被(.*)给包括进去了。这是因为星号(*)和加号(+)都属于贪婪型元字符。所谓贪婪型元字符,就是从头到尾的去整体匹配,而不是从头开始顺序匹配,遇到第一个能结束匹配的位置就停止。如果要正确截取上面的例子,就需要使用懒惰型元字符,或者说是*或+的懒惰型版本。方法是在贪婪型元字符后面加个问号(?)。

*的懒惰型是*?
+的懒惰型是+?
{n,n}的懒惰型是{n,n}?

以此类推。注意,指定重复字符范围的语法{,}也属于贪婪型,它的懒惰型版本是{,}?

将前面的正则改为:

<B>.*?</B>

就可以完美匹配了。当然作为html标记本身来说,还包括<b></b>这样的小写方式,如果需要匹配的话,应该是:

<[Bb]>.*?</[Bb]>

问题解决了。

BTW:我的小软件《每日笔记》获取天气的问题终于解决了,虽然可能不是最好的方法,但毕竟结果是正确了。

天气显示正常!

留言