正则表达式高级用法总结

2018/06/12

本文原创作者:Cloud Chou. 欢迎转载,请注明出处和本文链接

先前对正则表达式的零宽断言,后向引用,贪婪与懒惰等特性望而生畏,最近对这些特性重新理解了一番,这样以后可以更好的使用这些特性

零宽断言

“零宽断言”的功能和元字符^,$的功能类似,是为了限制匹配字符串必须出现在特殊的位置才能说这个字符串匹配成功,比如: ^是匹配字符串的开始,$匹配字符串的结束

零宽断言则是为了限制匹配的字符串右边有某个特征,或者左边有什么特征

  1. 右边有某个特征,英文是: look forward, 也叫先行零宽断言

    示例:

    正则表达式re(?=gular),其中(?=gular)就是先行零宽断言,如果用它去扫描字符串”a regular expression” 其中 re gular中的re和expression中的re都能匹配re 但是正则表达式re(?=gular)这个先行零宽断言要求跟在re右边的必须是gular,所以只有regular中的re是符合条件的, 因为零宽断言只是为了限制位置,不会匹配掉任何字符,所以扫描时最终只有regular单词中的re符合条件,只会匹配这个re,而不会是regular

    这里的先行或者说是look forward的含义是,当正则表达式扫描引擎扫描时,发现re匹配后,需记住当前的位置,然后用零宽断言(?=gular)继续向前扫描,看零宽断言是否和继续往前扫描的字符串匹配,如果零宽断言匹配才能说这个re是匹配的

    所以这里先行的含义是指继续向前扫描,看是否和零宽断言中的表达式是否匹配,通过这种方式来限制匹配的字符串必须出现在和零宽断言匹配的位置之前

  2. 左边有某个特征,就是look backforwad,也叫后行零宽断言

    示例:

    比如正则表达式(?<=\w)re,其中(?<=\w)就是后行零宽断言,如果用它去扫描字符串”regex represents regular expression”
    regex represents regular expression中的re都能匹配re 但是正则表达式(?<=\w)这个后行零宽断言要求跟在re左边的必须有一个字母或者数字,所以只expression中的re是符合条件的 因为零宽断言只是为了限制位置,不会匹配掉任何字符,所以扫描时最终只有expression单词中的re符合条件,只会匹配这个re,而不会是expression

    这里的后行或者说是look backward的含义是,当正则表达式扫描引擎扫描时,发现re匹配后,需记住当前的位置,然后用零宽断言(?<=\w),向后扫描,看零宽断言是否和向后扫描的字符串匹配,如果零宽断言匹配成功,才能说这个re是匹配的

    所以这里后行的含义是指回过头来向后扫描,看是否和零宽段严重的表达式是否符合匹配,通过这种方式来限制匹配的字符串必须出现在和零宽断言匹配的位置之后

明白了零宽断言中的先行(look forward)和后行(look backward)的含义后,我们再看正向和负向的含义,正向和负向的区别其实就是要求位置(先行或者后行)必须满足某个特性或者必须不满足某个特征

  1. 正向(?=xxx)

    正向要求限定的位置(左边或者右边)上必须满足某个特征,示例:

    1
    
    对”regex represents regular expression”这个字符串,要想匹配除regex和regular中的re,可以用”re(?g)”,该表达式限定了re右边的位置,这个位置后面必须是字符g。
    
  2. 负向(?!xxx)

    负要求限定的位置(左边或者右边)上必须不满足某个特征,示例:

    1
    
    对”regex represents regular expression”这个字符串,要想匹配除regex和regular之外的re,可以用”re(?!g)”,该表达式限定了re右边的位置,这个位置后面不是字符g。
    

总结

零宽断言有两个维度

  1. 先行或者后行,也叫look forward 或者 look backward,含义是限制左边或者限制右边

  2. 正向或者负向, 含义是指必须满足某个特征,或者必须不满足某个特征

这两个维度一共组成了4种零宽断言:

  1. 先行正向零宽断言 (?=xxx) 限制匹配的字符串右边必须满足某个特征
  2. 先行负向零宽断言 (?!xxx) 限制匹配的字符串右边必须不满足某个特征
  3. 后行正向零宽断言 (?<=xxx) 限制匹配的字符串左边必须满足某个特征
  4. 后行负向零宽断言 (?<!xxx) 限制匹配的字符串左边必须不满足某个特征

为了记忆方便,可这样记:

如果需要限制匹配的字符串出现的位置必须满足某个特征,可使用零宽断言,如果限制右边,则使用先行零宽断言(?xxx),如果限制左边则使用后行零宽断言(?<xxx),如果必须满足某个特征则使用正向匹配(?=xxx),否则使用负向匹配(?!xxx)

分组的特殊用法总结

分组有很多特殊用法,很多用法会使用?

分类 代码/语法 说明
捕获 (exp) 匹配exp,并捕获文本到自动命名的组里 后面还可使用\number的形式来后向引用这个分组 比如: \b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty
  (?<name>exp) 匹配exp,并捕获文本到name的组里 也可以写成(?’name’exp) 后面还可使用\k<name>的形式来后向引用这个分组 比如\b(?\w+)\b\s+\k\b也可以用来匹配重复的单词,象 go go
  (?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号
零宽断言 (?=exp) 先行正向匹配
  (?<=exp) 后行正向匹配
  (?!exp) 先行负向匹配
  (?<!exp) 后行负向匹配
  (?#comment 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

贪婪与懒惰

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:

a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)

为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。

懒惰限定符:

代码/语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

参考材料

  1. 正则表达式30分钟入门教程

  2. 正则表达式的先行断言(lookahead)和后行断言(lookbehind)

¥打赏5毛

取消

感谢您的支持,我会继续努力的!

扫码支持
赏个5毛,支持我把

打开支付宝扫一扫,即可进行扫码打赏哦

本篇目录