正则表达式——括号

用字符组和量词可以匹配引号字符串,也可以匹配 HTML tag,如果需要用正则表达式匹配身份证号码,依靠字符组和量词能不能做到呢? 

身份证号码是一个长度为15或18个字符的字符串,如果是15位,则全部由数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字,也可能是x。规则非常明确,可以尝试编写正则表达式了。

首位是数字,不能为0 [1-9]
除去首末2位,剩下13位或16位,且都是数字 d{13,16}
末位可能是数字,也可能是x [0-9X]


整个表达式是[1-9]\d{13,16}[0-9x],它的匹配如例3-1所示。

例3-1 身份证号码的匹配

idCardRegex = r"^[1-9]\d{13,16}[0-9x]$"

re.search(idCardRegex, "110101198001017032") != None # => True

re.search(idCardRegex, "1101018001017016") != None   # => True

re.search(idCardRegex, "11010119800101701x") != None # => True

看来,果然能够匹配各种形式的身份证号码,应该没问题。不过这还不够,这个正则表达式应该保证身份证号码的字符串能够匹配,其他字符串不能够匹配,例3-2展示了非身份证号码的匹配情况。 

 例 3-2 身份证号码的错误匹配

re.search(idCardRegex, "1101011980010176") != None # => True

re.search(idCardRegex, "110101800101701x") != None # => True

这两个字符串分明不是身份证号码(第一个有16位长,第二个虽然有15位长,但本尾是x),却都匹配了。这是为什么呢?仔细观察所用的正则表达式,会发现两点原因:第一表示除去首尾两位,中间的部分长度可能在13~16之间,而不是“长度要么为13,要么为16”;第二,最后的[0-9x]只应该对应18位身份证号码的情况,但是在这个表达式中,它也可以对应到15位身份证号码,而15位身份证号码的末位是不能为x的!

虽然字符串的长度是可变的,但是除去第一位和最后一位,中间部分的长度必须明确指定,只能是13或者16,而不能使用量词{13,16};另外,末尾一位到底是[0-9](也就是\d)还是[0-9x],取决于长度——如果长度是15位,则是\d;如果长度是18位,则是[0-9x]。区分两种情况分别考虑,要更加清楚一些。

15 位身份证号码 [1-9]\d{14}

18 位身份证号码 [1-9]\d{14}\d{2}[0-9x]

正则表达式改写为 [1-9]\d{14}(\d{2}[0-9x])?

 

括号的这种功能,叫做分组(grouping)

分组是非常有用的功能,因为使用正则表达式时经常会遇到并没有直接相连,但确实存在联系的部分,分组可以把这些概念上相关的部分“归拢”到一起,以免割裂。

/foo/bar_qux.php

之所以会乱套,根源在于有些元素虽然是“不一定出现”的。可是,“不一定出现”的元素之间却是有关联的:“不一定出现”的元素虽然没有直接相连,却是“要么同时出现,要么同时不出现”的关系。这时候就要梳理清楚逻辑关系,用括号的分组功能把各种分支情况归拢到一起。

/foo是必须出现的;之后存在两种可能; /bar.php或者/bar_ qux.php。前一种情况中,开头的/、控制器名bar、结尾的.php是必须出现的;在后一种情况中,开头的/、控制器名bar、下画线_、模块名qux、结尾的.php是必须出现的。

正确urlPatternRegex = r"^[a-zA-Z]+(/[a-zA-Z]+(_[a-zA-Z]+)?\.php)?$"

 例 3-8 完整匹配 E-mail 地址的正则表达式

emailRegex = r"^[-\w.]{0.64}@([-a-zA-Z0-9]{1,63}\.)*[-a-zA-Z0-9]{1,63}$"

 

posted @ 2018-10-20 14:19  gaara724  阅读(1056)  评论(0编辑  收藏  举报