正则表达式学习3
2.“或”匹配
有的时候,我们要取消某段文字中的加粗、斜体等效果,我们想匹配所有的“<b>”、“</b>” 或者 “<i>”、“</i>”,然后把它们替换成空,仅利用之前的知识,我们只能
进行两次匹配和替换,一次是“</?b>”,一次是“</?i>”。
在正则表达式中,可以使用“|”将一个表达式拆分成两部分“reg1|reg2”,它的意思是:匹配所有符合表达式reg1的文本 或者 符合表达式reg2的文本。
对于本节提出的问题,可以这样进行解决:
Text
The <b>text of</b> this row is bold.
The <i>text of</i> this row is italic.
RegEx
</?i>|</?b>
Result
The <b>text of</b> this row is bold.
The <i>text of</i> this row is italic.
3.在子模式中使用“或”匹配
从上面的定义应该可以看出,“|”分隔的是整个表达式,而有的时候,我们希望分隔的是一个表达式的一部分,比如说,我们想要匹配 “1900”到“2099”的所有年份。
Text
1932 is supposed to be matched as a whole, but it is matched only part of it.
2055 is mathced in the right way.
3019 is out of range, but it's still matched partly.
RegEx
19|20\d{2}
Result
1932 is supposed to be matched as a whole, but it is matched only part of it.
2055 is mathced in the right way.
3019 is out of range, but it's still matched partly.
可以看到,表达式“19|20\d{2}”要么匹配“19”,要么匹配“20\d{2}”。而我们希望的是匹配“19\d{2}”或者“20\d{2}”,当然,我们可以改写上面的表达式为“19\d{2}
|20\d{2}”来完成,但是,利用本章所讲述的子模式,可以更加简洁地完成这个过程。
Text
1932 is supposed to be matched as a whole, but it is matched only part of it.
2055 is mathced in the right way.
3019 is out of range, but it's still matched partly.
RegEx
(19|20)\d{2}
Result
1932 is supposed to be matched as a whole, but it is matched only part of it.
2055 is mathced in the right way.
3019 is out of range, but it's still matched partly.
这次,我们得到了想要的结果,使用子模式可以简化匹配表达式。
NOTE:有点类似于数学中的提取公因式:2*3 + 7*3 = (2+7)*3 --> 19\d{2}|20\d{2} = (19|20)\d{2}
4.嵌套子模式
子模式可以再继续嵌套子模式,产生更加功能强大的匹配能力。比如,我们要匹配 1900年1月1日 到 2000年1月1日 除过闰年外的所有正确日期。
我们先对这个匹配模式做一个分析:
1. 首位可以是19也可以是20; Reg:19|20
2. 当是19的时候,后面可以是00到99中任意数字; RegEx:19\d{2}|20
3. 当是20的时候,只能匹配00; Reg:19\d{2}|2000
4. 月份可以是1到9,或者10到12;Reg:(19\d{2}|2000)-([1-9]|1[0-2])
因为天数与月份相关,所以将 ([1-9]|1[0-2]) 拆分为下面三个子模式:
5. 当月份是2的时候,天数是28; Reg:2-([1-9]\b|1\d|2[0-8])
6. 1、3、5、7、8、10、12月,天数是31; Reg:([13578]|1[02])-([1-9]\b|[12]\d|3[01])
7. 4、6、9、11月,天数是30; Reg:([469]|11)-([1-9]\b|[12]\d|30)
NOTE:注意上面日期部分的匹配,分成了两部分,月和日;对于月来说,如果我们要匹配大月(31天的月),写法是:[13578]|1[0-2];而日期部分,比如说要匹配31天,
它又由三部分组成:[1-9]表示1号到9号;[12]\d表示10号到29号;3[01]表示30号到31号。
还有个地方需要注意:单词边界问题,如果你这样写表达式:2-([1-9]|1\d|2[0-8]),对于2-29 这样不应该匹配的日期,会匹配它合法的部分 2-29,因为2-2满足2-[1-
9]。回顾下我之前讲述的内容,我们还必须规定,当天数是个位数时,它必须处于单词边界[1-9]\b。
组合一下,得到的月份和天数的模式是:
2-([1-9]\b|1\d|2[0-8])|([13578]|1[02])-([1-9]\b|[12]\d|3[01])|([469]|11)-([1-9]\b|[12]\d|30)
再结合上面年的部分,得到最终的结果:
(19\d{2}|2000)-(2-([1-9]\b|1\d|2[0-8])|([13578]|1[02])-([1-9]\b|[12]\d|3[01])|([469]|11)-([1-9]\b|[12]\d|30))
Text
These dates are matched: 1900-1-1、1928-2-28、1931-11-30、2000-1-1、1999-10-30
These dates are not matched: 1900-1-32、1928-2-29、2000-01-1、1982-12-08
RegEx
(19\d{2}|2000)-(2-([1-9]\b|1\d|2[0-8])|([13578]|1[02])-([1-9]\b|[12]\d|3[01])|([469]|11)-([1-9]\b|[12]\d|30))
Result
These dates are matched: 1900-1-1、1928-2-28、1931-11-30、2000-1-1、1999-10-30
These dates are not matched: 1900-1-32、1928-2-29、2000-01-1、1982-12-08
后向引用
1.理解后向引用
我们还是一如既往地从最简单的开始。假设我们要进行这样一个匹配:找出下面文本中所有重复的单词,以便日后进行替换。
Is the cost of of gasline going up?
我们看到:“of of”重复了,我们需要找出它:
Text
Is the cost of of gasline going up?
RegEx
of of
Result
Is the cost of of gasline going up?
很显然,匹配结果满足了我们的要求,在这里使用全字匹配是为了后面好说明。
现在,如果 up 也重复出现了,句子变成这样:
Is the cost of of gasline going up up?
我们就需要改写表达式成这样:
Text
Is the cost of of gasline going up up?
RegEx
(of|up) (of|up)
Result
Is the cost of of gasline going up up?
NOTE:我们可以使用更简洁的表达式:((of|up)\b ??){2},但是为了后面好说明,这里我们还是使用全字匹配。
关于这个表达式,首先记住,\b只是对边界进行限制,不匹配任何字符。
如果写做((of|up)\b ){2},则无法匹配“up up”,因为它要求up后面必须出现一个空格“ ”,而本句中,up后面紧跟了一个问号;
如果写成((of|up)\b ?){2},因为是贪婪匹配,如果of后出现空格,就会匹配之。两个of的匹配将变成“of of ”。通常,我们会替换它成一个“of”,这样,就会出现
“ofgasline”的情况。
最终,我们把贪婪匹配改成惰性匹配:((of|up)\b ??){2}
可以看到,对这个句子来说,这样匹配没有问题。但是我们匹配的文本往往比这个复杂,让我们给上面的文本再添一句话。
Text
Is the cost of of gasline going up up?
Look up of the TV, your mobile phone is there.
RegEx
(of|up) (of|up)
Result
Is the cost of of gasline going up up?
Look up of the TV, your mobile phone is there.
不幸的是,对于下面不应该匹配的“up of”也进行了匹配。而我们需要的是这样一种匹配:当前面是“of”的时候,后面跟的也是“of”;当前面是“up”时,后面跟的也是
“up”,只有这样的情况才去匹配它。换言之,后面所需要匹配的内容是前面的一个引用。
正则表达式中,使用“\数字”来进行后向引用,数字表示这里引用的是前面的第几个子模式。
我们回头在看本节第一个例子,我们也可以这样写:
Text
Is the cost of of gasline going up?
RegEx
(of) \1
Result
Is the cost of of gasline going up?
这里,表达式“(of) \1”使用了后向引用,意思是:“\1”代表前面第一个匹配了的子模式的内容,对于本例,因为前面子模式只可能匹配“of”,那么“\1”等价于“of”,整个
表达式相当于“of of”。
现在,我们使用后向引用,对刚才出现“up of”问题的表达式加以纠正:
Text
Is the cost of of gasline going up up?
Look up of the TV, your mobile phone is there.
RegEx
(of|up) \1
Result
Is the cost of of gasline going up up?
Look up of the TV, your mobile phone is there.
这一次,我们获得了预期的效果,“(of|up) \1”的含义是:如果前面 子模式1 匹配了“of”,那么“\1”就代表“of”;如果 子模式1 匹配了 “up”,那么“\1”就代表
“up”,整个表达式相当于“of of|up up”。