概述和入门
- 1、从Regexpal开始
- 2、匹配北美电话号码
- 3、用字符组来匹配数字
- 4、使用字符组简写式
- 5、匹配任意字符
- 6、捕获分组和后向引用
- 7、使用量词
- 8、括选文字符
1、从Regexpal开始
首先介绍一下Regexpal网站(http://www.regexpal.com)。在Google Chrome或者Mozilla Firefox之类的浏览器中打开该网站,可以看到如图所示的网站页面。
接近浏览器窗口的顶部有一个文本区,下方还有一个更大的文本区。顶部的文本区是用来输入正则表达式的,下方的文本区是用来输入主题或目标文本的。目标文本即要从中匹配字符串的文本。
2、匹配北美电话号码
现在我们要写一个正则表达式匹配北美电话号码。在Regexpal的下方文本框中键入以下电话号码:
707-827-7019知道这是哪里的电话号码吗?这是O’Reilly Media的电话号码。
接下来用正则表达式匹配这个号码。有很多方法都可以做到,但首先只要在上方的文本框键入这个号码本身,也就是与下方文本框的内容完全一致即可(先忍耐一下,别泄气):
707-827-7019这时,你应该看到下方文本框中的电话号码从头到尾都以黄色被高亮显示。如果你的结果也是这样(如图所示),那么一切正常。
刚刚你所写的正则表达式是用字符串字面值(stringliteral)来匹配目标字符串的。所谓字符串字面值,就是字面上看起来是什么就是什么。
现在将上方文本框的号码删除,然后只键入数字7。你看到什么了?现在只有数字7高亮显示。正则表达式中的字面值(数字)7与目标文本中数字7的四个实例匹配。
3、用字符组来匹配数字
如果想同时匹配电话号码中的所有数值或者只匹配特定的数值,该怎么办呢?
在上方的文本框中尝试以下表达式:
[0-9]下方文本框中所有的数值(准确地说,应该是数字)以黄色和蓝色交替高亮显示。正则表达式[0-9]对正则表达式处理器传递的信息是“匹配0到9范围内的任意数字”。
正则表达式将方括号视为特殊的元字符(metacharacter),因此方括号不参与匹配。元字符是在正则表达式中有特殊含义的字符,也是保留字符。[0-9]这种形式的正则表达式称做字符组(character class),有时也叫字符集(character set)。
可以对数字的范围进行进一步限定。用更具体的一组数字也能得到同样的结果,比如:
[012789]这个字符组只会匹配列出的数字,即0、1、2、7、8、9。在上方的文本框中试一试,下方文本框中的相应数字同样会被交替标亮。
要匹配任意10位以连字符分隔的北美电话号码,可以使用以下正则表达式:
[0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]
这是可以的,但太长了。更好的方法是采用简写形式。
4、使用字符组简写式
就像在本章开始看到的那样,\d可以像[0-9]一样匹配任意阿拉伯数字。请在上方文本框内试一试,和之前的表达式一样,下方的数字都被标亮了。这种正则表达式叫做字符组简写式(character shorthand),也叫转义字符(character escape)。但后一种称谓很容易造成误解,我会尽量不用。至于原因,稍后我再解释。
可以使用以下表达式来匹配电话号码中的任意数字:
\d\d\d-\d\d\d-\d\d\d\d
重复\d三次和四次就可以分别匹配三个和四个数字。该表达式中的连字符是一个字面值,因此会被原样匹配。
除了和上面表达式一样,使用连字符本身(-)来匹配连字符之外,也可以用转义的大写D(\D),它匹配任何一个非数字字符。
以下示例使用了\D(而没有使用连字符本身)来匹配连字符:
\d\d\d\D\d\d\d\D\d\d\d\d
这次整个电话号码包括连字符又都被标亮了。
5、匹配任意字符
还可以用点号(.)来匹配那些讨厌的连字符:
\d\d\d.\d\d\d.\d\d\d\d
点号(英文句号)是一个通配符,可以匹配任意字符(但某些情况下不能匹配行起始符)。以上示例中的正则表达式匹配了连字符,但它也可以匹配百分号(%):
707%827%7019
或者是竖线(|):
707|827|7019
亦或其他字符。
如前所述,点号一般不匹配行起始符,比如换行符(U+000A)。然而,有很多方法可以使点号匹配行起始符,之后我会展示。这通常叫做dotall选项。
6、捕获分组和后向引用
本节我们使用捕获分组(capturing group)来匹配电话号码中的某一部分。然后使用后向引用(backreference)对分组中的内容进行引用。要创建捕获分组,先将一个\d放在一对圆括号中,这样就将它放入了一个分组中,后面可以用\1来对捕获的内容进行后向引用:
(\d)\d\1\1对括号内分组捕获的内容进行了反向引用。这个正则表达式匹配的是区号707。以下是对该表达式的详细分析:
(\d)匹配第一个数字并将其捕获(数字7);\d匹配第二个数字(数字0)但没有捕获,因为没有括号;\1对捕获的数字进行反向引用(数字7)。
现在可以用一个分组和几个后向引用对整个电话号码进行匹配:
(\d)0\1\D\d\d\1\D\1\d\d\d
但这还不够简洁美观。下一节我们会尝试更好的方法。
7、使用量词
现在用另一种语法来匹配电话号码:
\d{3}-?\d{3}-?\d{4}花括号中的数字表示待查找的数字出现的次数。包含数字的花括号是一种量词(quantifier)。花括号本身用做元字符。
问号是另一种量词,在以上表达式中表示连字符是可选的。也就是说,连字符可以不出现或只出现一次。还有其他的量词,例如加号(+)表示“一个或多个”,星号(*)表示“零个或多个”。
使用量词能让正则表达式变得更简洁:
(\d{3,4}[.-]?)+
对,加号表示出现一次或多次。这个正则表达式表示括号里的模式出现一次或多次,括号里的模式匹配三位或四位数字,后跟一个连字符或一个点号。
你有没有头晕呢?我希望没有。下面逐一解释表达式中的每一项:
- 左圆括号
(为捕获分组的起始符; - 反斜杠
\为字符组简写式的起始符(对之后的字符进行转义); - 字符
d为字符组简写式的结束符(d匹配0到9范围内的任意数字); - 左花括号
{为量词起始符; - 数字
3为匹配的最小数量; - 逗号
,隔开不同的数量; - 数字
4为匹配的最大数量; - 右花括号
}为量词的结束符; - 左方括号
[为字符组的起始符; - 点号
.(匹配点号本身); - 连字符
-匹配连字符的本身; - 右方括号
]为字符组的结束符; - 问号
?表示量词“零个或一个”; - 右圆括号
)为捕获分组的结束符; - 加号
+表示量词“一个或多个”。
这个表达式能用但不完全对,因为它只能匹配3位或4位的数字,而不管是否符合电话号码的格式。好吧,犯错会让我们印象深刻,进步得更快。
我们来改进一下:
(\d{3}[.-]?){2}\d{4}
这个表达式匹配的字符串是连续两个无括号的三位数字,每三位数字后可以带连字符也可以不带,最后是一个四位数字。
8、括选文字符
最后这个正则表达式表示第一个3位数字可以带也可以不带括号,即区号是可选的:
^(\(\d{3}\)|^\d{3}[.-]?)?\d{3}[.-]?\d{4}$
为了便于理解,我们再依次看一下表达式中的各项:
- 出现在正则表达式起始位置或者竖线(|)之后的脱字符
^,表示电话号码会出现在一行的起始位置; - 左括号
(为捕获分组的起始符; \(表示左括号本身;\d匹配一位数字;\d之后的{3}是量词,表示匹配三位数字;\)匹配右括号本身;- 竖线符
|表示选择,也就是从多个可选项中选择一个,换句话说,它表示“匹配一个不带括号的区号或一个带括号的区号”; - 脱字符
^匹配行起始位置; \d匹配一位数字;{3}是表示匹配三位数字的量词;[.-]?匹配一个可选的点号或连字符;- 右括号
)为捕获分组的结束符; - 问号
?表示分组可选,即分组中的前缀可有可无; \d匹配一位数字;{3}表示匹配三位数字的量词;[.-]?匹配另一个可选的点号或连字符;\d匹配一位数字;{4}是表示匹配四位数字的量词;- 美元符
$匹配行结束位置。
这个表达式最终匹配十位的北美电话号码,而且括号、连字符或者点号都是可选的。你可以试试不同格式的电话号码,看看它能否匹配。