Python 2.7的手册中,官方的解释为:
(?(id/name)yes-pattern|no-pattern) Will try to match with yes-pattern if the group with given id or name exists, and with no-pattern if it doesn’t. no-pattern is optional and can be omitted. For example, (<)?(\w+@\w+(?:\.\w+)+)(?(1)>) is a poor email matching pattern, which will match with '<[email protected]>' as well as '[email protected]', but not with '<[email protected]'.
New in version 2.4.
下面先来详细解释一下这段话的含义:
1.(?(id/name)yes-pattern|no-pattern) 的作用是:
- 对于给出的id或者name,先尝试去匹配 yes-pattern部分的内容;
- 如果id或name条件不满足,则去匹配no-pattern部分的内容;
这句话听着还是很拗口的,或者说一下子还是很难懂的。至少我之前第一次,以及后来的很多次,看了之后,也一样没有完全搞懂。
后来经过一些实践,算是终于有所了解其中的含义了。
所以此处有必要,再详细解释一下:
此处的name或id,是针对(当前位置的)条件性匹配之前的,某个已经通过group去分组的内容
其中:
如果是有命名的分组,即named group,则对应的该分组就有对应的name,即此处所指的就是对应的name;
如果是无命名的分组,即unnamed group,则对应的该分组也有对应的分组的编号,称为group的number,也叫做id,对应的就是这里的id;
注:
相关内容,如果不了解,请先去看:
【教程】详解Python正则表达式之: (?P<name>…) named group 带命名的组
【教程】详解Python正则表达式之: (…) group 分组
所以,搞清楚了这里的id或name,才好理解这段话。
接着解释就是,前面已经通过对应的group去匹配对应的内容了,对应的有group的id或name,
如果前面的group匹配成功,则此处,就执行yes-pattern的匹配;
如果前面group匹配不成功,即没有找到符合该的group内容,则就匹配no-pattern;
其中,yes-pattern和no-pattern,都是对应的,普通的正则表达式,用来匹配所需的内容。
此时,才算基本明白其所要说的含义了。
2.根据语法,很明显,如果存在no-pattern,则前面要有个竖杠’|’,用来分隔yes-pattern和no-pattern
如果此处,不想写,不需要写no-pattern,则也是可以的,也是可以省略掉的,对应的,也就不需要再写那个竖杠’|’了。
3.上面给出了一个例子:
(<)?(\w+@\w+(?:\.\w+)+)(?(1)>)
此处,还是先来解释解释吧。
其可以分解为:
- (<)?:就是匹配小于号这个符号,后面的?表示可以有,可以没有;
- (\w+@\w+(?:\.\w+)+):最外层的括号内是\w+@\w+(?:\.\w+)+,其又可以分为:
- \w+@\w+:\w+表示尽可能多的匹配字母数字下划线,中间是匹配@字符本身,所以就是希望匹配到xxx@xxx
- (?:\.\w+)+:前面是括号括起来的一组,加号+表示尽可能多的匹配括号内的。看完下面解释就明白了,此处就是匹配尽可能多的.xxx的内容
- ?:\.\w+:此处,是(?:xxx)形式,即一个非捕获组,匹配xxx的内容,此处即去匹配\.\w+,即一个点,然后是字母数字下划线,即.xxx的内容。很容易理解,此处就是希望匹配到.com之类的内容
注:非捕获匹配,不了解的可以去参考:【教程】详解Python正则表达式之: (?:…) non-capturing group 非捕获组
- ?:\.\w+:此处,是(?:xxx)形式,即一个非捕获组,匹配xxx的内容,此处即去匹配\.\w+,即一个点,然后是字母数字下划线,即.xxx的内容。很容易理解,此处就是希望匹配到.com之类的内容
- (?(1)>) :此处,就是真正我们此处最关系的,选择性匹配了。参考本身的语法:(?(id/name)yes-pattern|no-pattern) ,此处中的1,就是对应id,即之前的第一个组,即(<);而(1)后面的>,就是此处的yes-pattern,而此处很明显,no-pattern是被省略了。所以没有’|’。所以,此处的含义就很清楚了,就是去匹配,如果最开始的编号为1的那个组,即(<)存在的话,即前面能找到<的话,此处后面也就去匹配有没有>,如果前面没有<,则后面就什么也不匹配。正真是符号我们所期望的,要么是[email protected],要么是[email protected]
很显然,此处这种匹配规则,是很不严谨的,对于很多复杂的邮箱地址,没法匹配到。所以,此处才说是“poor email matching pattern”,即相对是一个不是那么好的email的匹配规则。
至此,终于把此处的内容解释完毕了。
但是,对于选择性匹配,还是没有很深刻的理解。
下面,就通过详细的代码,来更加深入的解释,选择性匹配,有什么用,以及如何用。
下面,就以之前给某人写的一个正则表达式为例,来说明,什么是条件性匹配。
先说,关于有些人不清楚,觉得选择性匹配,有何用。那么此处,就来举个例子,相对比较实用的例子:
希望去写一个匹配规则,匹配用于输入的数字,其中,输入的内容只能是数字,也可能有小数点,如果有小数点,则小数点后面,此处特意限制为最多2位。
针对如上的需求,就可以用到选择性匹配去实现了:
如下是详细的代码演示:
#!/usr/bin/python # -*- coding: utf-8 -*- """ 【教程】详解Python正则表达式之: (?(id/name)yes-pattern|no-pattern) 条件性匹配 https://www.crifan.com/detailed_explanation_about_python_regular_express_yes_or_no_conditional_match Version: 2012-11-17 Author: Crifan """ import re; #需求: #类似于检测(最多两位小数的)数字的合法性: #所有的字符都是数字 #如果有小数点,那么小数点后面最多2位数字 testNumStrList = { #合法的数字 '12.34', '123.4', '1234', #非法的数字 '1.234', '12.', '12.ab', '12.3a', '123abc', '123abc456', } for eachNumStr in testNumStrList: #下面这个是不严谨的,会导致: #1.234 -> 只会去判断234,所以检测出整数部分是234,无小数 #123.4 -> 只会去判断4,所以检测出整数部分是4,无小数 #123abc456 -> 只会去判断456,所以检测出整数部分是456,无小数 #foundValidNumStr = re.search("(?P<integerPart>\d+)(?P<foundPoint>\.)?(?P<decimalPart>(?(foundPoint)\d{1,2}))$", eachNumStr); #下面这个也是不严谨的,会导致: #1.234 -> 只去判断1.23,所以检测出整数是1,小数是23 #12. -> 只会去判断12,所以检测出整数是12,无小数 #123abc456 -> 只会去判断123,所以检测出整数是123,无小数 #12.ab -> 只会去判断12,所以检测出整数是12,无小数 #123abc -> 只会去判断123,所以检测出整数是123,无小数 #12.3a -> 只会去判断12.3,所以检测出整数是12,小数是3 #foundValidNumStr = re.search("^(?P<integerPart>\d+)(?P<foundPoint>\.)?(?P<decimalPart>(?(foundPoint)\d{1,2}))", eachNumStr); #下面这个,更不严谨,会导致中间只要有数字,那么基本上都会去匹配到,和实际的期望,差距最大 #foundValidNumStr = re.search("(?P<integerPart>\d+)(?P<foundPoint>\.)?(?P<decimalPart>(?(foundPoint)\d{1,2}))", eachNumStr); #下面这个才是正确的 foundValidNumStr = re.search("^(?P<integerPart>\d+)(?P<foundPoint>\.)?(?P<decimalPart>(?(foundPoint)\d{1,2}))$", eachNumStr); #也可以写成下面这样: #foundValidNumStr = re.search("^(?P<integerPart>\d+)(\.)?(?P<decimalPart>(?(2)\d{1,2}))$", eachNumStr); #这个也是同样的效果 #print "foundValidNumStr=",foundValidNumStr; if(foundValidNumStr): integerPart = foundValidNumStr.group("integerPart"); decimalPart = foundValidNumStr.group("decimalPart"); print "eachNumStr=%s\tis valid numebr ^_^, integerPart=%s, decimalPart=%s"%(eachNumStr, integerPart, decimalPart); else: print "eachNumStr=%s\tis invalid number !!!"%(eachNumStr);
当然,对于上述的正则表达式,即使理解了,但是可能很多人还是对于,如何从无到有的去写出来,觉得无从下手,所以,可以再去参考这个帖子:
【教程】以Python中的re模块为例,手把手教你,如何从无到有,写出相对复杂的正则表达式
【总结】
通过上面的代码,我们就更加容易理解了,对应的,选择性匹配组,是如何实现的。
实际中,当有类似的情况时,我们就可以充分利用这个高级搜索规则,去轻松的匹配,获得我们所需的内容了。
转载请注明:在路上 » 【教程】详解Python正则表达式之: (?(id/name)yes-pattern|no-pattern) 条件性匹配