【读书】正则指引-3-括号
发布时间:2020-12-14 01:25:30 所属栏目:百科 来源:网络整理
导读:分组 小括号(以下称为括号)可以提供的第一个功能就是 分组(grouping) 。 通过之前的学习,可以知道量词用于限定之前元素的出现次数,这个元素可能是一个字符,也可能是一个字符组,还可能是一个表达式。如果把一个表达式用括号包围起来,就形成了括号内
分组小括号(以下称为括号)可以提供的第一个功能就是分组(grouping)。通过之前的学习,可以知道量词用于限定之前元素的出现次数,这个元素可能是一个字符,也可能是一个字符组,还可能是一个表达式。如果把一个表达式用括号包围起来,就形成了括号内的表达式,而这种表达式通常被称为“子表达式”。 换句话说,子表达式就是利用括号分组功能形成的表达式。 比如,(ab)+表示希望字符串ab重复出现一次以上。(对比ab+的含义) 分组的作用是可以准确表达“长度只能是m或 n”的语义。 分组功能使用场景:
多选结构解决15和18位身份证匹配问题其实有两种思路。第一种(上面给出的正则表达式),是将18位号码多出的3位“合并”到匹配15位号码的表达式中。第二种,则是直接将15位和18位身份证号分开处理。同样还是使用括号,但使用的是括号的第二个功能,多选结构(alternative)。多选结构的形式是(…|…),即在括号内以竖线|分隔开多个子表达式,这些子表达式也叫做多选分支。在一个多选结构内,多选分支的数目没有限制。在匹配时,整个多选结构被视为单个元素,只要其中某个子表达式能够匹配,整个多选结构的匹配就成功。如果所有子表达式都不能匹配,则整个多选结构匹配失败。 所以,身份证号码匹配的第二种方式为:([1-9]d{14}|[1-9]d{14}d{2}[0-9x]) 多选结构使用场景:
引用分组括号不仅仅能把有联系的元素归拢起来并分组,还有其他作用,即使用括号之后,正则表达式会保存每个分组真正匹配的文本,等到匹配完成后,可以通过group(num)之类的方法“引用”分组在匹配时捕获的内容。其中num表示对应的编号,括号分组的编号规则是从左向右计数,从1开始。这就是括号的第三个功能,捕获分组(capturing group)。这种括号叫做捕获型括号。一般来说,正则表达式匹配完成之后,都会得到一个表示“匹配结果”的对象,对它调用获取分组的方法,传入分组编号num,就可以得到对应分组匹配的文本。num的编号是从1开始的,不过编号为0的分组也是默认存在的,其对应整个表达式匹配的文本。 关于括号存在嵌套的问题:无论括号如何嵌套,分组的编号都是根据开括号出现顺序来计数的,开括号是从左向右起第多少个开括号,整个括号分组的编号就是多少。 引用分组是使用场景:
在提取日期 2010-12-22 这个内容时,正确的正则表达式为 (d{4})-(d{2})-(d{2}) 而错误的表达式可能被写成 (d){4}-(d{2})-(d{2}) 引用分组捕获的文本,不仅仅用于数据提取,也可以用于替换。 在各种语言中都有类似substitute(pattern,replacement,string)这种形式的正则替换命令。其中replacement处可以使用引用分组,形式是num(有些语言中为$num,下文同,本文中只写作num),其中num为对应分组的编号。同时要明白replacement处为普通字符串,在某些语言中,需要特别指明replacement使用原生字符串才行,否则num这种引用分组在普通字符串中会被当做不合法的转义序列处理。 另外,如果想在replacement中引用整个表达匹配的文本,不能使用 ,即便使用原生字符串也不行。一种变通策略是给整个表达式加上一对括号,之后使用1来引用。 反向引用反向引用(back-reference)主要用于检测重叠出现的内容,即允许在正则表达式内部引用之前的捕获分组匹配的文本。其使用形式也是num。反向引用的使用场景:
各种引用的记法上面说过,对分组进行引用的时候使用num这类表示法,同时还说过,还有另外一些语言使用了不同的表示方式。如下图所示一般情况下,我们认为$num要好于num。原因在于,$0可以准确表示“第0个分组(也就是整个表达式匹配的文本)”,而 则不行,因为不少语言的字符串中,num本身可能就是一个有意义的转义序列,表示值为num的ASCII码字符,所以 会被解释成“ASCII编码为0的字符”。 二义性问题:无论采用num还是$num进行引用都会遇到二义性问题,如果出现了10,它到底表示第10个捕获分组10,还是第1个捕获分组1之后跟着一个字符0? 各种语言的解决方式有所不同,Python和PHP中提供了特定的表示法来专门解决这类问题(细节略)。而Java、Ruby和JavaScript中则是通过制定规则来解决这类问题的:如果是一位数,则引用对应的捕获分组;如果是两位数且存在对应捕获分组时,引用对应的捕获分组,如果不存在对应的捕获分组,则引用一位数编号的捕获分组。 简单思考一下就可以发现,后面这几种语言的策略存在一个问题无法解决:如果存在编号为10的捕获分组,无法用10表示“编号为1的捕获分组和字符0”,因为此时10表示的必然是编号为10的捕获分组。在实际开发中,尤其是进行文本替换时有时确实会遇到这个问题,在现有的规则下是无解的。所幸的是,一般情况下,我们根本不会用到太多的捕获分组(即基本上用不到10以上的分组);另外,已经有越来越多的语言提供了命名分组,它可以彻底解决这个问题。 命名分组使用数字编号来标识捕获分组存在不够直观,括号多了容易搞错序号,引用时不够方便的问题。所以,一些语言和工具提供了命名分组(named grouping),可以将它看成另一种捕获分组。命名分组在每种语言中各有不同,此处不做展开。另外,由于历史原因和后向兼容性,即便使用了命名分组,每个命名分组同时也具有数字编号,其编号规则没有变化。 非捕获分组前面介绍了括号的三种主要用途,需要指出的是,各种用途间不是彼此独立的,而是互相重叠的:单纯的分组可以视为“只包含一个多选分支的多选结构”;整个多选结构也会被视为单个元素,可以由单个量词限定。最重要的是,无论是否需要引用分组,只要出现了括号,正则表达式在匹配时就会把括号内的子表达式存储起来,提供引用。如果并不需要引用,保存这些信息无疑会影响正则表达式的性能;如果表达式比较复杂,要处理的文本又很多,更可能严重影响性能。为解决这种问题,正则表达式提供了非捕获分组(non-capturing group),非捕获分组类似普通的捕获分组,只是在开括号后紧跟一个问号和冒号(?:…),这样的括号叫做非捕获型括号。它的作用只是限定量词的作用范围,不捕获任何文本。在引用分组时,分组的编号同样会按照开括号出现的顺序从左向右递增,只是必须以捕获分组为准,非捕获分组会略过。 补充括号的转义括号的转义要求和括号相关的所有三个元字符(、)、|都必须转义(、)、|。URL RewriteURL Rewrite是常见web服务器都具备的功能,用来进行网址的转发。例子如下外部访问 URL http://www.example.com/blog/2006/12 内部实现 http://www.example.com/blog/posts.php?year=2006&month=12 这样重写的好处是隔离了外部接口和内部实现,方便修改;也有利于提供更有意义、更直观的URL。 一般来说,URL Rewrite都是使用转发规则实现的,每条转发规则对应一类URL,以正则表达式解析并提取出所有需要的信息,重组之后再转发。 学习URL Rewrite的使用可以参考当前的各类主流Web服务器的配置,此处略。 总结本篇将《正则指引》的第三章内容进行了概括总结,下次讲解正则表达式中的断言。参考资料《正则指引》:余晟。(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |