• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 断言(前瞻后瞻)

    一个断言就是一个对当前匹配位置之前或之后的字符的测试,它不会实际消耗任何字符。简单的断言代码有\b\B\A\Z\z^$等等。更加复杂的断言以子组的方式编码。它有两种类型:前瞻断言(从当前位置向前测试)和后瞻断言(从当前位置向后测试)。

    前瞻断言

    一个断言子组的匹配还是通过普通方式进行的,不同在于它不会导致当前的匹配点发生改变。

    • 前瞻断言中的正面断言以?=开始,断言此匹配为真
    • 前瞻断言中的消极断言以?!开始。

    比如,\w+(?=;)匹配一个单词紧跟着一个分号(;),但是匹配捕获的最终结果中不会包含分号foo(?!bar)匹配所有后面没有紧跟“bar”的“foo”字符串。

    注意:一个类似的模式(?!foo)bar,它不能用于查找之前出现所有不是“foo”的“bar”匹配,它会查找到任意的“bar”出现的情况,因为(?!foo)这个断言在接下来三个字符时“bar”的时候是永远都TRUE

    后瞻断言

    • 后瞻断言中的正面断言以?<=开始。
    • 后瞻断言中的消极断言以?<!开始。

    比如,(?<!foo)bar用于查找任何前面不是“foo”的“bar”。

    匹配固定长字符串

    后瞻断言的内容被严格限制为只能用于匹配定长字符串。但是,如果有多个可选分支,它们不需要拥有相同的长度。比如(?<=bullock|donkey)是允许的,但是(?<!dogs?|cats?)将会引发一个编译期的错误。

    在最上级分支可以匹配不同长度的字符串是允许的。相比较于 perl 5.005 而言,它会要求多个分支使用相同长度的字符串匹配。(?<=ab(c|de))这样的断言是不允许的,因为它单个的顶级分支可以匹配两个不同的长度,但是它可以接受使用两个顶级分支的写法(?<=abc|abde)这样的断言实现,对于每个可选分支,暂时将当前位置移动到尝试匹配的当前位置之前的固定宽度处。如果在当前没有足够的字符就视为匹配失败。

    后瞻断言与一次性子组结合使用可以用来匹配字符串结尾。一个例子就是在一次性子组上给出字符串结尾。

    多个断言可以同时出现

    多个断言(任意顺序)可以同时出现。比如(?<=\d{3})(?<!999)foo匹配前面有三个数字但不是“999”的字符串“foo”。注意:每个断言独立应用到对目标字符串该点的匹配。首先它会检查前面的三位都是数字,然后检查这三位不是“999”。这个模式不能匹配“foo”前面有三位数字然后紧跟 3 位非 999 共 6 个字符的字符串,比如,它不匹配“123abcfoo”。匹配“123abcfoo”这个字符串的模式可以是(?<=\d{3}…)(?<!999)foo

    这种情况下,第一个断言查看(当前匹配点)前面的 6 个字符,检查前三个是数字,然后第二个断言检查(当前匹配点)前三个字符不是“999”。

    任意复杂度嵌套

    断言可以以任意复杂度嵌套。比如(?<=(?<!foo)bar)baz匹配前面有“bar”但是“bar”前面没有“foo”的“baz”。另外一个模式(?<=\d{3}…(?<!999))foo则匹配前面有三个数字字符紧跟 3 个不是 999 的任意字符的“foo”。

    不捕获子组内容,仅仅是预览

    断言子组时非捕获子组,并且不能用量词修饰,因为对同一件事做多次断言是没有意义的。如果所有的断言都包含一个捕获子组,那么为了在整个模式中捕获子组计数的目的,它们都会被计算在内。然而,子字符串的捕获仅可以用于正面断言,因为对于消极的断言是没有意义的。通俗的讲,它不匹配字符串,而是匹配一个位置。

    将断言计算在内,可以拥有的最大子组数量是 200 个。

    匹配不包含字符串的写法

    比如匹配不包含字符串ab的行写法就应该如下:

    1. 首先写下(?!ab),意思是匹配一个位置,该位置后面没有ab。
    2. 其次写出(?!ab).,意思就是该位置后面可以跟任何一个字符(除了ab),但这个正则表达式只匹配单个字符,因此该模式匹配某个位置后的单个字符,该字符是a且后面是b,否则该字符就得不到匹配。
    3. 再次写出((?!ab).)*,意思是零次或者多次的匹配。

    如果匹配不包含ab的某一行的话,就是如下写法:^((?!ab).)*$

    上篇:后向引用

    下篇:一次性子组