4. 零宽断言
1) 常用零宽断言语法
(?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
2) 这四个语法是用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。
 (?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。
(?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。
注:断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。
以下举例说明——
3) 表达式:\b\w+(?=ing\b)
所需语法:\b——匹配一个单词的开始或结束,也就是指单词和空格间的位置;\w——匹配任意一个字母或数字或下划线或汉字等;+——重复前一个字符或一组表达式一次或更多次;(?=exp)——匹配exp前面的位置;
含义说明:匹配以ing结尾的单词的前面部分(除了ing以外的部分),
查找结果:比如查找I'm singing while you're dancing.时,它会匹配sing和danc。如图21所示
 
图21
4) 表达式:(?<=\bre)\w+\b
所需语法:(?<=exp)——;\w——匹配任意一个字母或数字或下划线或汉字等;+——重复前一个字符或一组表达式一次或更多次;\b——匹配一个单词的开始或结束,也就是指单词和空格间的位置;;
含义说明:匹配以re开头的单词的后半部分(除了re以外的部分),
查找结果:比如在查找reading a book时,它匹配ading。如图22所示
 
图22
5) 表达式:((?<=\d)\d{3})+\b
所需语法:(?<=exp)——匹配exp后面的位置;\d——匹配任意一位数字,等价于[0~9];{n}——重复n次,只对邻近的字符或一组表达式起作用;+——重复前一个字符或一组表达式一次或更多次;\b——匹配一个单词的开始或结束,也就是指单词和空格间的位置;
含义说明:假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})+\b,用它对1234567890进行查找时结果是234567890。
查找结果:比如对“1234567890”进行查找时结果是“234567890”;如图23所示
 
图23
6) 表达式:(?<=\s)\d+(?=\s)
所需语法:(?<=exp)——匹配exp后面的位置;\d——匹配任意一位数字,等价于[0~9];+——重复前一个字符或一组表达式一次或更多次;(?=exp)——匹配exp前面的位置;
含义说明:匹配以空白符间隔的数字(注:不包括这些空白符)。
查找结果:查找类似“556 99 886333”这样的字符串,结果为“99”;如图24所示
 
图24
5. 负向零宽断言
宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。主要用于确保某个字符没有出现,但并不想去匹配它的时候。以下是负向零宽断言的例子:
1) 表达式:\b\w*q(?!u)\w*\b
所需语法:\b——匹配一个单词的开始或结束,也就是指单词和空格间的位置;\w——匹配任意一个字母或数字或下划线或汉字等;*——重复前一个字符或一组表达式零次或更多次;(?!exp)——零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp;
含义说明:匹配包含后面不是字母u的字母q的单词(注意:不能使用\b\w*q[^u]\w*\b这样的表达式,因为[^u]一定要占据一个字符,如果碰到以q结尾的像“benq”这样的单词,就会出错,而负向零宽断言(?!u)就不会占有任何字符)。
查找结果:查找到类似“benq,open”的字符串;如图25所示
 
图25
2) 表达式:\d{3}(?!\d)
所需语法:\d——匹配任意一位数字,等价于[0~9];{n}——重复n次,只对邻近的字符或一组表达式起作用;(?!exp)——零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp;
含义说明:匹配三位数字,而且这三位数字的后面不能是数字;
查找结果:查找类似“556g,dd334”这样的字符串,能找到“556”和“334”的结果;如图26所示
 
图26
3) 表达式:\b((?!abc)\w)+\b
所需语法:\b——匹配一个单词的开始或结束,也就是指单词和空格间的位置;(?!exp)——零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp;\w——匹配任意一个字母或数字或下划线或汉字等;+——重复前一个字符或一组表达式一次或更多次;
含义说明:匹配不包含连续字符串abc的单词。
查找结果:查找类似“fdsafsdg,fabcadfda”,找到“fdsafsdg”这样的结果;如图27所示
 
图27
4) 表达式:(?<![a-z])\d{7}
所需语法:(?<!exp)——匹配前面不是exp的位置;\d——匹配任意一位数字,等价于[0~9];{n}——重复n次,只对邻近的字符或一组表达式起作用;
含义说明:匹配前面不是小写字母的七位数字。
查找结果:查找类似“B1234567,c9876543”这样的字符串,能找到“1234567”这样的结果;如图28所示
 
图28
5) 表达式:(?<=<(\w+)>).*(?=<\/\1>)
所需语法:(?<=exp)——匹配exp后面的位置;\w——匹配任意一个字母或数字或下划线或汉字等;+——重复前一个字符或一组表达式一次或更多次;
.——匹配除换行符以外包括空格和Tab键的任意字符;*——重复前一个字符或一组表达式零次或更多次;(?=exp)——匹配exp前面的位置;\——转义符,将特殊字符转化为普通字符,取消后面跟着的字符的特殊意义;
含义说明:匹配不包含属性的简单HTML标签内里的内容。(<?(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是<b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\ /,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(注意:不包括前缀和后缀本身)。
查找结果:匹配类似“<body>abcd</body>”的字符串,能找到“abcd”这样的结果;如图29所示
 
图29
6. 注释:
1) (?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读。以下是注释的例子:
2) 2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。
(?<=    # 断言要匹配的文本的前缀
<(\w+)> # 查找尖括号括起来的字母或数字(即HTML/XML标签)
)       # 前缀结束
.*      # 匹配任意文本
 (?=    # 断言要匹配的文本的后缀
<\/\1>   # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签
)        # 后缀结束
7. 贪婪与懒惰
1) 常用懒惰匹配表达式
*?——重复任意次,但尽可能少重复
+?——重复1次或更多次,但尽可能少重复
?? ——重复0次或1次,但尽可能少重复
{n,m}?——重复n到m次,但尽可能少重复
{n,}? ——重复n次以上,但尽可能少重复
2) 当正则表达式中包含能接受重复的限定符时,通常会在使整个表达式能得到匹配的前提下匹配尽可能多的字符。比如用表达式“b*c”查找“bbcbc”,它将匹配整个字符串“bbcbc”,这就是贪婪匹配。
3) 如果有时候我们想匹配尽可能少的字条,就需要用到懒惰匹配。观察上面的常用懒惰表达式可知,其实懒惰匹配模式,就是在表达式的后面加上一个问号“?”。这样“.*?”就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。比如“bbcbc”字符串,表达式“b.*?c”匹配最短的,以b开始,以c结束的字符串。匹配结果就变为“bbc”。
注意:为什么第一个匹配是bbc(第一到第三个字符)而不是bc(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权。
(全文完)