条件子组

条件子组语法


条件子组(?(if)then_pattern|else_pattern)或者(?(if)then_pattern),允许我们创建带有条件的正则表达式。如果(if)为true,正则引擎就会去匹配then_pattern。否则,如果存在else_pattern,就会去匹配else_pattern。 而对于if可以是一个断言(?(?=regex)then_pattern|else_pattern),也可以是一个数字(?(1)then_pattern|else_pattern)

条件如果是一个断言,这个很好理解,因为断言本身就是一个判断。如果条件是一个数字,就是表示这个数字对应的捕获子组匹配成功的话,条件true,如果匹配失败,则条件false。

示例


下面我们看一个例子/(a)?b(?(1)c|d)/,这个正则可以匹配字符串"abc""bd",也可以匹配字符串"abd"中的"bd";但是不能匹配"bc"

首先我们看匹配"abc"。正则(a)可以匹配第一个字符'a';然后是正则b匹配下一个字符'b';接下来进入条件表达式,因为是一个数字1,第一个子组a匹配成功,这里的条件为真,所以正则引擎匹配then_pattern c和下一个字符c。可以匹配成功,到此整个正则表达式匹配成功。

下面我们看匹配字符串"bd"。正则(a)匹配第一个字符'b'失败,但是后面跟着的是?,允许(a)匹配失败,所以继续下一个正则b,匹配第一个字符'b'成功。下面进入正则的条件(1),因为前面没有子组捕获成功,所以此时条件为false。正则引擎走else_pattern,使用d匹配下一个字符'd',成功。至此整个正则表达式匹配成功。

对于为什么不能匹配"bc"。各位可以按照上面的遍历过程自行分析。

最有意思的应该是为什么能匹配字符串"abd"中的"bd"。注意,这里只是匹配其中的"bd",而不是匹配成功正字字符串"abd"。有匹配到内容,说明正则表达式最后是成功的。但是按照我们上面的说法,即然(a)匹配成功了,那(1)为真,应该走then_pattern,c不能和字符'd'匹配,所以应该是匹配失败的。说的没错,到这个地方逻辑都是没问题的。但是需要注意的是,else_pattern在此时还没有被尝试,虽然前面子组捕获成功,条件为 true。但是此时也只是then_pattern c被尝试了,它虽然失败了,整个正则表达式却还没有完成。所以正则引擎会从头开始匹配正则表达式,对于目标字符串则会前进一个字符,也就是从字符'b'开始。再次从(a)开始匹配,此时匹配失败。引擎继续一下个正则b和字符'b'匹配成功。因为前面子组捕获失败,所以条件为false,此时走else_pattern,正则d和下一个字符d匹配成功。整个正则此时已经走完,最后匹配成功"bd"。 通过这个例子我们可以看到,其实条件子组和我们正常的编程语言的 if(){}else{}还是有区别的。在条件子组中,then_pattern和 else_pattern 更像是可选路径,类似于(then_pattern|else_pattern)。在整个正则表达式中,所有的正则其实都是条件,就类似于我们编程语言中的if(){}else{}中if后面的()里的条件,而不是{}里的实体。所以对于整个条件子组(?(if)then_pattern|else_pattern) 可以理解成 (if) && then_pattern | else_pattern。如果(if)为true,则还要去判断then_pattern,如果它也为true,那|前面部分就为true了,对于|后面的条件也就不用再判断了,因为条件是只要有一个为true,整个表达式就为true。而如果(if) 和 then_pattern其中有一个为false,那前面部分就是false了。对于|后面的就会去进行判断,当else_pattern为true,整个条件表达式同样也为true。而向我们上面匹配"abd"这个例子就是 (if)为true,但是then_pattern为false的情况,所以else_pattern 还是会去进行判断。只是正则引擎的判断方式不一样,需要对整个正则进行回溯。但是道理是一样的。

对于(if)是断言的情况,我们自己也可以举些例子按照上面的方法进行分析。

还有个问题需要说明一下,对于条件子组,只能有两个可选项——then_patternelse_pattern。如果超过了两个,会产生编译错误。当然了,对于then_pattern和else_pattern本身可以是有多个可选项的。也就是说then_pattern和else_pattern必须是原子的。例如:(?(if)(then1|then2|then3)|(else1|else2|else3)) 这是可以的。但是如果是(?(if)then1|then2|then3|else1|else2)这样的,在编译的时候就会报错了。

查看笔记

扫码一下
查看教程更方便