正则表达式学习2
匹配多个字符
应该了解,上面所介绍的不管简单也好,复杂也好,都只是匹配单个字符,如果需要匹配一个很长的字符串,而组成这个字符串的每个字符都比较复杂(没有诸如\d这样的简写方式),那么,可以想象,一个表达式会多么复杂。
回顾一下匹配Web中颜色的例子,我们的正则表达式写法是这样的:“#[0-9a-f][0-9a-f] [0-9a-f][0-9a-f][0-9a-f][0-9a-f]”。应该想到,如果有办法确定匹配的单个字符的个数就好了。本章中,将讨论使用正则表达式来进行多个字符的匹配。
1.匹配一个或多个
正则表达式中,可以在单个字符(比如"j")、字符组(比如"[abcde]")、特定字符类型(比如"\d")、单个任意字符(即".")后面加"+",来表示匹配一个或多个(换言之,至少一个)字符组成的字符串。
拿上面的例子来说,对于 “j+” 有:
Text
jimmy、jjjimmy、simmy、immy、enjoy、enjjoy
RegEx
j+
Result
jimmy、jjjimmy、simmy、immy、enjoy、enjjoy
对于 “[abcde]+”有:
Text
abandon、abroad、bbbbb、aaa、edcab、bbb、jskal
RegEx
[abcde]+
Result
abandon、abroad、bbbbb、aaa、edcab、bbb、jskal
我们现在考虑一个实际的例子,我的电子邮件的写法:jimmy_dev@163.com ,如果没有本章的知识,匹配邮件地址是不可能的,因为,我们无法确定的知道邮件地址的长度。现在,如果我们要匹配这个邮件地址,结合之前的知识 和 本小节的新内容,很容易得出这样的表达式:
"\w+@\w+\.\w+"。
Text
somebody@domain.net
jimmy_dev@sina.com.cn
RegEx
\w+@\w+\.\w+
Result
somebody@domain.net
我们发现,这样的匹配并不完整,对于多级域名的匹配不完整。我们回想一下之前的内容,很容易想到,我们需要将"."也加入到字符组中,最后,我们将"\w+"改写成"[\w\.]+"。
前面说过:"-"只在字符组"[]"区间内才是一个元字符,在"[]"以外是普通字符。在此处,"."在"[]"区间以外才是元字符,用以匹配任意单个字符,而在"[]"区间以内,它就变成了一个普通字符。所以,"[\w\.]"可以简写成"[\w.]"。
Text
somebody@domain.net
jimmy_dev@sina.com.cn
RegEx
[\w.]+@[\w.]+\.\w+
Result
somebody@domain.net
这次,可以看到能够正确地匹配多级域名了。
2.匹配零个或多个字符
让我们再次回到刚才匹配城市图片的例子(字符组和反义字符组那一节),我们知道,不管是使用"city[0-3]\.jpg“还是"city[^0-3]\.jpg","city"与".jpg”之间总是出现一个字符的,而有的时候,我们允许它们之间可以不出现字符,比如说:现在我要求可以匹配"city.jpg",那么,该如何完成呢?
正则表达式中,可以在单个字符(比如"j")、字符组(比如"[abcde]")、特定字符类型(比如"\d")、单个任意字符(即".")后面加 "*"来表示匹配零个或多个字符组成的字符串,可以看出,其使用方法与"+"完全相同。
对于上面的例子,我们可以写出下面的表达式
Text
I like city.jpg、city1.jpg、city365.jpg most.
I suppose cityss.jpg and city_1.jpg are the worst ones.
RegEx
city\d*\.jpg
Result
I like city.jpg、city1.jpg、city365.jpg most.
I suppose cityss.jpg and city_1.jpg are the worst ones.
我们看到,表达式"city\d*\.jpg"匹配这样的字符串:以"city"开头,后面紧跟零个或多个"0-9的数字字符,然后,以".jpg"结尾。
我们再次回到上面的匹配Email的例子中。表达式是这样的:"[\w.]+@[\w.]+\.\w+",看上去似乎没有问题,但是,我们发现对于 “.jimmy.dev@hotmail.com”这样的地址,它也能够匹配。而我们知道,“.”不应该出现在邮件的第一个字符位置上。
结合本节的知识,我们知道,Email的起首一定是 一个或多个 字母或数字组合,而后面可以是 零个或多个 字母数字与“.”的组合。所以,我们修改匹配为如下:
Text
.jimmy.dev@hotmail.com is not a valid email format,while jimmy.dev@hotmail.com
RegEx
\w+[\w.]*@[\w.]+\.\w+
Result
.jimmy.dev@hotmail.com is not a valid email format,while jimmy.dev@hotmail.com
尽管这次对正确的Email地址部分进行了匹配,便由于“.jimmy.dev@hotmail.com”是作为一个整体出现,而它并不是一个合法的地址,所以我们通常希望的操作是完全不去匹配它,即认为它并不是一个有效Email,而不是去匹配它合法的部分。如何解决这个问题,留待后面字符边界说明。
3.匹配零个或一个字符
现在加入我们要对单词的正确性进行匹配,对于"flower"这个单词来说,不管是单数"flower"还是复数"flowers",都是正确的写法,而对于"flowersss"则是不正确的。
使用本章前两章的知识,无法完成这样的匹配。
对于"flowers+"来说,它不能匹配"flower",因为它只能匹配"flower”后面有一个或多个"s"的单词,于是,"flowers"和"flowersss"都可以被匹配。
对于"flowers*"来说,它虽然能够匹配"flower"和"flowers",但是一样可以匹配"flowersss"。
正则表达式中,使用"?"来匹配零个或一个字符。其使用方式与"+"和"*"相同。
Text
These flowers are very beautiful, hope you like them.
This flower is so beautiful, seems like smiling to you.
RegEx
flowers?
Result
These flowers are very beautiful, hope you like them.
This flower is so beautiful, seems like smiling to you.
很明显,本章的所讲述的多字符匹配中,“+”、“*”、“?”都是元字符,如果要对它们进行匹配,需要使用“\”进行转义:“\+”、“\*”、“\?”。
4.匹配指定数目字符
尽管"?"、"+"、"*"这三个元字符的出现解决了很多问题,但是,它们并不完整:
(1)没有办法指定最多匹配多少个字符,比如说,我们要匹配手机号码,那么应该是11个数字,而"+"和"*"会匹配尽可能多的数字,不管是17个还是18个都认为是正确的。
(2)没有办法指定最少匹配多少个字符,“+”、“*”、“?”,所能提供的最少字符,要么零个,要么一个。
4.1 匹配固定数目的字符
正则表达式中,可以在单个字符(比如"j")、字符组(比如"[abcde]")、特定字符类型(比如"\d")、单个任意字符(即".")后面加"{数字}"来表示匹配零个或多个字符组成的字符串。
eg:使用"\d{3}",可以匹配从000到999,这1000个数。而使用"a{6}",则可以匹配"aaaaaa"(也只能匹配它,因为"a"是固定字符)。
现在我们考虑一个更复杂的例子,假如我们要匹配手机号码,那么它的规则是:首位为“1”,第二位为“3或者5”,后面9位任意数字。那么它的匹配应该是这样的:
Text
13991381592 is a valid telephone number.
1369188351 is too short.
The second number of 17991381592 is out of range.
The character “d” in 13d91381592 is not allowed.
The first character in 63991381592 should be 1 constantly.
RegEx
1[35]\d{9}
Result
13991381592 is a valid telephone number.
1369188351 is too short.
The second number of 17991381592 is out of range.
The character “d” in 13d91381592 is not allowed.
The first character in 63991381592 should be 1 constantly.
4.2 匹配区间以内数目的字符
我们再次考虑 000-999 的匹配:“\d{3}”,尽管它没有错,但它只能匹配精确地匹配3位:000、001、002 … 100、101 … 999。而通常,我们需要对于 0、10、99 这样的数也能够匹配,这时,就需要指定可以匹配 1到3位的数字。
正则表达式中,使用“{最小数目,最大数目}”的语法来实现,它的使用方式与上一节介绍的匹配固定数目字符的语法相同。
Text
0、10、111、001、789、1234 are all matched numbers.
0d、0h、1xx、99. are not matched.
RegEx
\d{1,3}
Result
0、10、111、001、789、1234 are all matched numbers.
0d、0h、1xx、99. are not matched.
NOTE:我们发现了两个问题:
1、我们不希望 “1234” 被匹配,然而,它被分成两部分“123”和“4”进行了匹配,如何不让它被匹配;
2、为什么要被分成123和4,而不是1和234?我们留待后面讨论。
注意两个特例:
1. 最小数目可以是0,所以 “{0,1}”,相当于 “?”。
2. 如果不限制最大数目,可以将最大数目设为空,所以“\d{1,}”相当于“+”;而“{0,}”相当于“*”。
“{”和“}”也是元字符,当我们需要对它们进行匹配的时候,使用“\”进行转义:“\{”和“\}”。
5.贪婪匹配和惰性匹配
5.1 贪婪匹配、惰性匹配概述
我们首先从字面意思上来理解一下贪婪匹配和惰性匹配:
贪婪匹配(greedy):它会匹配尽可能多的字符。它首先看整个字符串,如果不匹配,对字符串进行收缩;遇到可能匹配的文本,停止收缩,对文本进行扩展,当发现匹配
的文本时,它不着急将该匹配保存到匹配集合中,而是对文本继续扩展,直到无法继续匹配 或者 扩展完整个字符串,然后将前面最后一个符合匹配的文本(也是最长的)保存起来到
匹配集合中。所以说它是贪婪的。
惰性匹配(lazy):它会匹配尽可能少的字符,它从第一个字符开始找起,一旦符合条件,立刻保存到匹配集合中,然后继续进行查找。所以说它是懒惰的。
光看上面的定义,我们很难有一个生动的认识,现在假设我们要匹配下面 <b>和</b> 之间的文本。为了做演示,尽管不符合HTML的定义,我们再加入一段<b>和
</c>之间的文本:
Text
Jimmy is a <b>junior</b> developer <b>living</b> in <b>xi'an</c>.
RegEx
<b>.*</b>
Result
Jimmy is a <b>junior</b> developer <b>living</b> in <b>xi'an</c>
这样匹配显然不是我们的初衷,它仅找到了一个匹配,而通常情况下,我们希望得到的是<b>junior</b> 和 <b>living</b> 两个匹配。
解决的办法,就是上面说到的惰性匹配,它的语法如下表所示:
贪婪匹配 | 惰性匹配 | 匹配描述 |
? | ?? | 匹配0个或1个 |
+ | +? | 匹配1个或多个 |
* | *? | 匹配0个或多个 |
{n} | {n}? | 匹配n个 |
{n,m} | {n,m}? | 匹配n个或m个 |
{n,} | {n,}? | 匹配n个或多个 |
对于本例,当我们使用惰性匹配时:
Text
Jimmy is a <b>junior</b> developer <b>living</b> in <b>xi'an</c>.
RegEx
<b>.*?</b>
Result
Jimmy is a <b>junior</b> developer <b>living</b> in <b>xi'an</c>.
现在,请对照着之前对它们的定义,并结合下面对匹配过程的分析,来理解 贪婪匹配 和 惰性匹配的匹配过程:
5.2 贪婪匹配的匹配过程:
Jimmy is a <b>junior</b> developer <b>living</b> in <b>xi'an</c>.//不匹配,收缩
immy is a <b>junior</b> developer <b>living</b> in <b>xi'an</c>. //不匹配,收缩
mmy is a <b>junior</b> developer <b>living</b> in <b>xi'an</c>. //不匹配,收缩
... //中间略
<b>junior</b> developer <b>living</b> in <b>xi'an</c>. // 找到可能匹配的,扩展
<b>junior</b> developer <b>living</b> in <b>xi'an</c>.// 找到可能匹配的,扩展
<b>junior</b> developer <b>living</b> in <b>xi'an</c>. // 找到可能匹配的,扩展
... // 中间略
<b>junior</b> developer <b>living</b> in <b>xi'an</c>. //找到一个匹配,但是并不保存到结果集中,而是继续进行扩展
<b>junior</b> developer <b>living</b> in <b>xi'an</c>
. ... //中间略
<b>junior</b> developer <b>living</b> in <b>xi'an</c>. //又找到匹配,继续扩展
... //中间略
<b>junior</b> developer <b>living</b> in <b>xi'an</c>. //字符串结束,将前面找到的最后一个匹配 <b>junior</b> developer <b>living</b> 保存到匹配结
果集中
5.3 惰性匹配的匹配过程
J // 不匹配,继续
Ji // 不匹配,继续
... // 中间略
Jimmy is a < // 找到可能匹配的字符,继续
Jimmy is a <b // 找到可能匹配的字符,继续
... // 中间略
Jimmy is a <b>junior</b> //找到匹配,保存到结果集中,继续进行剩下的文本。
d // 不匹配,继续
de // 不匹配,继续
developer < // 找到可能的匹配,继续
developer <b // 找到可能的匹配,继续
... // 中间略
developer <b>living</b> //找到匹配,保存到结果集中,继续进行剩下的文本。
i //不匹配,继续in //不匹配,继续
in < // 找到可能的匹配,继续
in <b // 找到可能的匹配,继续
... // 中间略
in <b>xi'an</c // 匹配失败,继续找
in <b>xi'an</c> // 不匹配,继续
in <b>xi'an</c>. // 字符串结束,匹配结束。一共找到了两个匹配<b>junior</b> 和<b>living</b>
我们回顾一下上面 “\d{1,3}” 匹配数字的例子,对于“1234”,当我们使用“\d{1,3}”时,进行的是贪婪匹配,它首先找到“123”(因为“1234”不符合),之后的“4”也符
合,所以,找到的匹配是“123”和“4”。
当我们使用“\d{1,3}?”匹配上面的例子,对于“1234”,这次是惰性匹配。首先,发现“1”符合,将“1”保存到匹配集合中;随后,依次发现“2”、“3”、“4”符合,并依
次保存到结果集中,最后,我们得到了四个匹配“1”、“2”、“3”、“4”。
5.4 值得注意的两个匹配模式
现在请回顾一下上面贪婪、惰性匹配语法的表,有两个匹配模式比较有意思:
一个是“{n}”,对于这种形式的匹配,由于它精确地要求匹配n个字符,所以无所谓贪婪还是惰性,尽管“{n}?”也是正确的匹配写法,但它的匹配结果总是与“{n}”相同。
还有一个就是“??”,它看上去比较古怪且不好理解,因为通常我们使用贪婪匹配的时候都是匹配多个,也就是“*”或者“+”之类的匹配,而这里是0个或1个,它的贪婪
与惰性匹配又是如何呢?我们还是看范例来说明:
Text
These flowers are for you, my beloved.
RegEx
flowers?
Result
These flowers are for you, my beloved.
我们来分析一下,在这个匹配中,匹配是这样的:首先需要匹配“flower”字符串,然后,可以有0个或者1个“s”,按上面讲述的,贪婪匹配匹配尽可能多的,所以flowers
被匹配了。
再来看看“??”这种匹配。
Text
These flowers are for you, my beloved.
RegEx
flowers??
Result
These flowers are for you, my beloved.
这次只匹配了“flower”,我想现在已经不用过多解释,你对这个怪异的匹配语法已经明白了。惰性匹配发现“flower”满足匹配0个的条件,于是将它保存到匹配结果集,然
后重新进行,匹配查找,直到字符串结束。
匹配边界
回忆一下刚才邮件地址的匹配,表达式“\w+[\w.]*@[\w.]+\.\w+”匹配了不合法的邮件地址“.jimmy_dev@163.com”中合法的部分,而通常,我们
希望它完全不去匹配它。这时,就有一个匹配边界的问题,我们希望:“\w+”必须出现在字符串的首位置,也就是字符串的边界。
邮件的例子稍显复杂,我们再看一个更简单的情况:
Text
The cat scattered its food all over the room.
RegEx
cat
Result
The cat scattered its food all over the room.
可见,通常情况下,我们只希望匹配 cat ,而不希望匹配 scattered 中出现的cat。
1.匹配单词边界
正则表达式中,可以在 字符 前加“\b”,来匹配其 后面 的字符位于字符串首位的字符。
NOTE:以后提到 字符,指:单个字符(比如“j”)、字符组(比如“[abcde]”)、特定字符类型(比如“\d”)、转义过的特殊字符“\[” 或者 单个任意字符(即“.”)。
我们来看一个最简单的例子:
Text
Attention: Zhang is the first name of JimmyZhang.
RegEx
\bz
Result
Attention: Zhang is the first name of JimmyZhang.
如同定义的那样:“\bz”匹配所有位于字符串首位的“z”,而对位于字符串中间的“z”则不进行匹配(“JimmyZhang”中的“Z”)。不过,我们很少如此简单地使用边界匹配
,而是将它加在一个表达式前面。
现在回到本章开头的例子,修改表达式,让它只匹配单词cat:
Text
The cat scattered its food all over the room.
RegEx
\bcat
Result
The cat scattered its food all over the room.
这一次,匹配正确,有了刚才单个字符的例子,现在这个表达式很好理解了:“\b”只规定了“c”这个字符必须出现在字符串首位,接下来需要出现字符“a”、“t”。这两个
字符的匹配与“\bc”无关,它们属于固定字符匹配的范畴。关于固定字符匹配,可见本文第一章。
但是这个匹配还存在问题,如果我们将上面的例子稍微做下修改,将“scattered”单词分成“s”和“cattered”:
Text
The cat s cattered its food all over the room.
RegEx
\bcat
Result
The cat s cattered its food all over the room.
可以看到,我们仅仅将“scattered”拆写成“s”和“cattered”,就又错误地匹配了“cattered”,因为它符合我们前面所说的匹配规则。
可见,如果需要仅匹配“cat”这个单词,我们还需要规定“t”必须出现在单词的末尾。
正则表达式中,可以在 字符 后加“\b”,来匹配其 前面 的字符位于字符串末位的字符。
Text
The cat s cattered its food all over the room.
The cat scat tered its food all over the room.
RegEx
cat\b
Result
The cat s cattered its food all over the room.
The cat scat tered its food all over the room.
NOTE:注意上面两个句子中“scattered”单词拆分位置不同。
可以看到,“cat\b” 不能匹配“cattered”之中的“cat”,却匹配了 “scat” 中的“cat”。这个匹配的过程就不再说明了,与上面类似。
显然,为了精确地匹配“cat”,我们需要在前后都加上字符边界,“\bcat\b”。
Text
The cat scat tered its food all over the room.
The cat s cattered its food all over the room.
RegEx
\bcat\b
Result
The cat scat tered its food all over the room.
The cat s cattered its food all over the room.
如果我简单地说:“\bcat\b”匹配完整地一个“cat”单词,那相当于什么也没说。我们综合上面讲述的,来分析一下它是如何匹配的:首先,“c”必须出现在字符串首位;
然后,紧跟一个“a”字符;最后,它必须以“t”结束。
2.边界及其相对性
2.1 边界的定义
讲了这么多,还漏掉了一个重要的内容:究竟什么才算边界?
通常情况下,以 空格、段落首行、段落末尾、逗号、句号 等符号作为边界,值得注意的是,分隔符“-”也可以作为边界。
Text
The cat s-cat-tered its food all over the room.
RegEx
\bcat\b
Result
The cat s-cat-tered its food all over the room.
这是什么原因呢?其实很好理解,从“-”的字面意思:分隔符,大致就可以想到了。实际上,在英语中,它是用来做单词分隔的。
NOTE:这里有个重要的搜索引擎优化常识,大家注意到本文档的命名,我采用的是:Regular-Expression-Tutorial.pdf,为什么不用下划线分隔,命名成
Regular_Expression_Tutorial.pdf 呢? 因为当搜索引擎看到“-”的时候,会把它视为一个空格“ ”,而看到下划线“_”的时候,会把它视为空字符“”,实际上,下划线
的正确叫法是“连字符”。于是,当我命名为Regular-Expression-Tutorial.pdf 时,搜索引擎看到的是: Regular Expression Tutorial.pdf,而当我命名
为Regular_Expression_Tutorial.pdf 时,搜索引擎看作RegularExpressionTutorial.pdf 。
可以看出,正则表达式在字符边界问题上 对“-”的处理方式 与 搜索引擎相同。
2.2 边界的相对性
请牢牢记住边界的这个特点:
a.当你对一个普通字符,比如“s”,设定边界的时候,它的边界是诸如空格、分隔符、逗号、句号等。
b.当你对一个边界,比如分隔符“-”或者“,”等,设定边界的时候,它的边界是普通字符。
我们先看第一种情况:
Text
aaaaxaaaa
aaa-x-aaa
RegEx
\bx\b
Result
aaaaxaaaa
aaa-x-aaa
这个和我们上面将的例子相似,和我们预想的一样,下面x被匹配了,因为“-”可以作为边界。
我们再看另一个例子:
Text
aaaa,aaaa
aaa-,-aaa
RegEx
\b,\b
Result
aaaa,aaaa
aaa-,-aaa
与上面唯一不同的是:这次我们匹配逗号“,”,而它本身也是一个边界,结果与上面完全相反。可见,对于“,”而言,它的边界是一个普通字母。
边界的相对性是很重要的,因为我们很多时候需要匹配诸如“<”这样的字符。
3.匹配非单词边界
和上面 匹配特定类型字符有些相似,有了“\b”,自然有“\B”,它用来匹配不在边界的字符。
我们继续拿上面的例子做示范,来看看“\Bcat”匹配的效果:
Text
The cat scat tered its food all over the room.
The cat s cattered its food all over the room.
RegEx
\Bcat
Result
The cat scat tered its food all over the room.
The cat s cattered its food all over the room.
它的匹配规则是这样的:字符“c”必须出现,但是不能位于字符串首位;随后跟两个固定字符“a”和“t”。
我们再对上面例子进行扩展:
Text
The cat scat tered its food all over the room.
The cat s cattered its food all over the room.
The cat scattered its food all over the room.
RegEx
\Bcat\B
Result
The cat scat tered its food all over the room.
The cat s cattered its food all over the room.
The cat scattered its food all over the room.
看了这么多例子,现在不用我讲你也应该明白了:首先,必须出现字符“c”,且不能位于字符串首位;接着,“c”后面必须出现字符“a”;最后,必须出现字符“t”,且它不
能位于字符串的末尾。
3.匹配文本边界
有的时候,我们想要匹配的字符串必须位于全部文本的首位,比如说XML文件的声明“<?xml version="1.0" encoding="UTF-8" ?>”;有的时候,需要匹配的字符串位
于全部文本的末尾,比如</html>。对于这种匹配,上面介绍的单词边界匹配就无能为力了。
3.1匹配文本首
在正则表达式中,可以在 匹配模式 的第一个字符前添加 “^”,以匹配 满足模式且位于全部文本之首的字符串。可以将它的匹配方式理解成这样:1、假设不
存在“^”,进行一个正常匹配,将所有匹配的文本保存到匹配集合中;2、在匹配集合中寻找位于 所搜索的文本 首位的匹配;3、从匹配集合中删除其他匹配,仅保留该匹配。
我们依然是从简单的例子看起:“^city\d?\.jpg”。
Text
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
RegEx
^city\d?\.jpg
Result
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
按照之前说的,它的匹配过程是这样:
1. 假设匹配模式是 city\d?\.jpg,对文本进行匹配。
2. 一共找到 6 个符合模式的文本:第一行 和 第二行的 city.jpg, city1,jpg 及 city9.jpg
3. 从所有匹配的文本中筛选出位于文本首位的匹配文本:即第一行的 city.jpg,删除所有其他匹配。
这里有个值得注意的地方,如果我们在第一行的city.jpg中添第几个空格,就破坏了这个匹配。
Text
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
RegEx
^city\d?\.jpg
Result
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
可见,没有找到任何匹配,所以,我们进行文本边界匹配时,通常还需要添加对空字符的处理:
Text
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
RegEx
^\s*city\d?\.jpg
Result
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
3.2匹配文本末
有匹配文本首的语法,自然有匹配文本末的语法。
在正则表达式中,可以在 匹配模式 的最后一个字符后添加 “$”,以匹配 满足模式且位于全部文本之末的字符串。
它的匹配方式 与 匹配文本首“^”相似,这里就不再详细说明了,只给出一个例子:
Text
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
RegEx
city\d?\.jpg\s*$
Result
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
city.jpg、city1.jpg are all beautiful pictures except city9.jpg
回顾下之前介绍的,可以看出:“\b”和“\B”是对 匹配模式(表达式) 中某个字符出现的进行位置(单词首位还是末位)进行限制。“^”和“$” 是对 整个待搜索文本 的
匹配模式(表达式) 出现位置(文本首位还是文本末位)进行限制。它们的关系是一小一大。
匹配子模式
可以看出,我们之前介绍的所有匹配模式(例如“+”、“*”、“{n,m}”),都是针对于某种 单个字符 的。
考虑这样一个例子:我们需要将HTML中两个或以上的“<br />”、“<br/>”、“<br>”全部替换成一个“<br />”。按照之前的例子,我们只能写出这样的表达式:
Text
This is the first line.<br>
This is the second line.<br><br/><br />
This is the third line.<br>>>>>
RegEx
<br\s*/?>{2,}
Result
This is the first line.<br>
This is the second line.<br><br/><br />
This is the third line.<br>>>>>
可以看到,匹配结果并不是我们想要的,这是因为 “{2,}”限制的是它之前的单个字符,在本例中也就是“>”的出现次数,而我们希望的,是整个“<br>”、“<br/>”或“<br />”。
1.子模式
在正则表达式中,可以使用“(”和“)”将模式中的子字符串括起来,以形成一个子模式。将子模式视为一个整体时,那么它就相当于一个单个字符。
就本例而言,我们希望子模式就是 “<br\s*/>”,按上面的定义,我们重新写模式:
Text
This is the first line.<br>
This is the second line.<br><br/><br />
This is the third line.<br>>>>>
RegEx
(<br\s*/?>){2,}
Result
This is the first line.<br>
This is the second line.<br><br/><br />
This is the third line.<br>>>>>
这次匹配了正确的文本,我们可以将匹配过程理解成这样:子模式“(<br\s*/?>)”首先匹配所有“<br>”、“<br/>”或“<br />”;然后,将每一个匹配结果视为一个整
体(相当于单个字符);接着,匹配这个整体连续出现两次或以上的文本。