正则表达式的⾼级⽤法分组和捕获
正则表达式⾼级⽤法(分组与捕获)
分组的引⼊:
对于要重复单个字符,⾮常简单,直接在字符后卖弄加上限定符即可,例如 a+ 表⽰匹配1个或⼀个以上的a,a?表⽰匹配0个或1个a。这些限定符如下所⽰:
X ?X ,⼀次或⼀次也没有
X *X ,零次或多次
X +X ,⼀次或多次
X { n }X ,恰好 n 次
X { n ,}X ,⾄少 n 次
X { n , m }X ,⾄少 n 次,但是不超过 m 次
但是我们如果要对多个字符进⾏重复怎么办呢?此时我们就要⽤到分组,我们可以使⽤⼩括号"()"来指定要重复的⼦表达式,然后对这个⼦表达式进⾏重复,例如:(abc)? 表⽰0个或1个abc 这⾥⼀ 个括号的表达式就表⽰⼀个分组 。
分组可以分为两种形式,捕获组和⾮捕获组。
捕获组
捕获组可以通过从左到右计算其开括号来编号 。例如,在表达式 (A)(B(C)) 中,存在四个这样的组:
0 (A)(B(C))
1 (A)
2 (B(C))
3 (C)
组零始终代表整个表达式
之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输⼊序列的每个⼦序列。捕获的⼦序
列稍后可以通过 Back 引⽤(反向引⽤) 在表达式中使⽤,也可以在匹配操作完成后从匹配器检索。
Back 引⽤ 是说在后⾯的表达式中我们可以使⽤组的编号来引⽤前⾯的表达式所捕获到的⽂本序列。注意:反向引⽤,引⽤的是前⾯捕获组中的⽂本⽽不是正则,也就是说反向引⽤处匹配的⽂本应和前⾯捕获组中的⽂本相同,这⼀点很重要。
【例】(["']).*\1
其中使⽤了分组,\1就是对引号这个分组的引⽤,它匹配包含在两个引号或者两个单引号中的所有字符串,如,"abc" 或 " ' " 或 ' " ' ,但是请注意,它并不会对" a'或者 'a"匹配。原因上⾯已经说明,Back引⽤只是引⽤⽂本⽽不是表达式。
⾮捕获组
以 (?) 开头的组是纯的⾮捕获 组,它不捕获⽂本 ,也不针对组合计进⾏计数。就是说,如果⼩括号中以?号开头,那么这个分组就不会捕获⽂本,当然也不会有组的编号,因此也不存在Back 引⽤。
我们通过捕获组就能够得到我们想要匹配的内容了,那为什么还要有⾮捕获组呢?原因是捕获组捕获的内容是被存储在内存中,可供以后使⽤,⽐如反向引⽤就是引⽤的内存中存储的捕获组中捕获的内容。⽽⾮捕获组则不会捕获⽂本,也不会将它匹配到的内容单独分组来放到内存中。所以,使⽤⾮捕
获组较使⽤捕获组更节省内存。在实际情况中我们要酌情选⽤。
1、⾮捕获组(?:Pattern)
它的作⽤就是匹配Pattern字符,好处就是不捕获⽂本,不将匹配到的字符存储到内存中,从⽽节省内存。
【例】匹配indestry或者indestries
我们可以使⽤indestr(y|ies)或者indestr(?:y|ies)
【例】(?:a|A)123(?:b)可以匹配a123b或者A123b
⾮捕获组有很多种形式,其中包括:零宽度断⾔和模式修正符
2、零宽度断⾔
(?= X ) X ,通过零宽度的正 lookahead
(?! X ) X ,通过零宽度的负 lookahead
(?<= X ) X ,通过零宽度的正 lookbehind
(?<! X ) X ,通过零宽度的负 lookbehind
这四个⾮捕获组⽤于匹配表达式X,但是不包含表达式的⽂本。
(?=X )零宽度正先⾏断⾔。仅当⼦表达式 X 在 此位置的右侧匹配时才继续匹配。也就是说要使此零宽度断⾔起到我们想要的效果的话,就必须把这个⾮捕获组放在整个表达式的右侧。例如,/w+(?=/d) 与后跟数字的单词匹配,⽽不与该数字匹配。此构造不会回溯。
(?!X)零宽度负先⾏断⾔。仅当⼦表达式 X 不在 此位置的右侧匹配时才继续匹配。例如,例如,/w+(?!/d) 与后不跟数字的单词匹配,⽽不与该数字匹配 。
(?
<=X)零宽度正后发断⾔。仅当⼦表达式 X 在 此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后⾯的 99 的实例匹配。此构造不会回溯。
(? <!X)零宽度负后发断⾔。仅当⼦表达式 X 不在此位置的左侧匹配时才继续匹配。例如,(?<!19)99 与不跟在 19 后⾯的 99 的实例匹配
上⾯都是理论性的介绍,这⾥就使⽤⼀些例⼦来说明⼀下问题:
【例1】正则表达式 (?<!4)56(?=9)
含义:匹配后⾯的⽂本56前⾯不能是4,后⾯必须是9组成。因此,可以匹配如下⽂本 5569 ,与4569不匹配。
【例2】提取字符串 da12bka3434bdca4343bdca234bm中包含在字符a和b之间的数字,但是这个a之前的字符不能是c;b后⾯的字符必须是d才能提取。
显然,这⾥就只有3434这个数字满⾜要求。那么我们怎么提取呢?
⾸先,我们写出含有捕获组的正则表达式:[^c]a\d*bdregex匹配
然后我们再将其变为⾮捕获组的正则表达式:(?<=[^c]a)\d*(?=bd)
3、模式修正符
以(?)开头的⾮捕获组除了零宽度断⾔之外,还有模式修正符。
正则表达式中常⽤的模式修正符有i、g、m、s、x、e等。它们之间可以组合搭配使⽤。
(?imnsx-imnsx: ) 应⽤或禁⽤⼦表达式中指定的选项。例如,(?i-s: ) 将打开不区分⼤⼩写并禁⽤单⾏
模式。关闭不区分⼤⼩写的开关可以使⽤(?-i)。有关更多信息,请参阅正则表达式选项。
【例1】(?i)ab
表⽰对(?i)后的所有字符都开启不区分⼤⼩写的开关。故它可以匹配ab、aB、Ab、AB
【例2】(?i:a)b
它表⽰只对a开启不区分⼤⼩写的开关。故它可以匹配ab和Ab。不能匹配aB和AB。
4、(?>Pattern)等同于侵占模式
匹配成功不进⾏回溯,这个⽐较复杂,与侵占量词“+”可以通⽤,⽐如:\d++ 可以写为 (?>\d+)。
【例】将⼀些多位的⼩数截短到三位⼩数:\d+\.\d\d[1-9]?\d+
在这种条件下 6.625 能进⾏匹配,这样做没有必要,因为它本⾝就是三位⼩数。最后⼀个“5”本来是给 [1-9] 匹配的,但是后⾯还有⼀个 \d+ 所以,[1-9] 由于是“?”可以不匹配所以只能放弃当前的匹配,将这个“5”送给 \d+ 去匹配,如果改为:
\d+\.\d\d[1-9]?+\d+
的侵占形式,在“5”匹配到 [1-9] 时,由于是侵占式的,所以不会进⾏回溯,后⾯的 \d+ 就匹配不到任东西了,所以导致 6.625 匹配失败。
这种情况,在替换时就有效了,⽐如把数字截短到⼩数点后三位,如果正好是三位⼩数的,就可以不⽤替换了,可以提⾼效率,侵占量词基本上就是⽤来提⾼匹配效率的。
把 \d+\.\d\d[1-9]?+\d+ 改为 \d+\.\d\d(?>[1-9]?)\d+ 这样是⼀样的。
【补充】js获取分组内容的⽅法:(可参考)
1、 arr[n] = str.match(reg); 或者 arr[n] = (str);
返回的匹配数组arr[n]中,arr[0]表⽰整个匹配,arr[1],arr[2].......分别表⽰各个分组的匹配结果
2、通过RegExp对象的静态属性来获取
RegExp.$1,RegExp.$2.........RegExp.$9 分别表⽰匹配到的第⼀个分组⾄第九个分组的内容
例:
var str = "adfasd324232sdfas";
alert(str);
var reg = new RegExp("([a-z]*)(\\d*)([a-z]*)");
var arr = str.match(reg);
alert(arr[0] + "===" + arr[1] + "===" + arr[2] + "===" + arr[3]); alert(RegExp.$1 + "-----" + RegExp.$2 + "----" + RegExp.$3);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论