现在的位置: 首页 > 综合 > 正文

正则表达式中的非捕获组(non-capturing)的用法

2018年03月23日 ⁄ 综合 ⁄ 共 2270字 ⁄ 字号 评论关闭

今天学了一下午的正则表达式(马士兵的那个视频教程),这RegularExpressions也算是一门小语言了,唯一让我不能理解的就是API里非捕获组中的那些用法,经过在论坛的查找学习,呵呵 ,现在终于理解了,下面与大家分享下。

 

捕获组(capturing group)是把多个字符当作一个单元对待的一种方式。通过把字符括在括号内创建捕获组。例如,正则表达式(dog)创建包含字母“d”、“o”和“g”的一个组。输入字符串和捕获组匹配的那一部分将被保存在内存中,以便以后通过反向引用再次使用。

而非捕获组就是输入字符串和捕获组匹配的那一部分将不被保存在内存中。这有什么用呢?当然是能够节省内存了,下面具体说下非捕获组里的那8个Special constructs(下面的内容大部分是我摘抄论坛上一个叫“火龙果”兄弟的回贴,然后再加上些我自己的理解):

 

以 (? 开头的都是非捕获组,都是不进行捕获的。

(?:XXX) 这个比较常用,在不需要捕获时可以替换 (XXX),比如:


匹配 June 和 July 可以写成:
Ju(ne|ly)

也可以写成
Ju(?:ne|ly)

前者会把 ( ) 内的在匹配后保留在内存中,而后者不会。

以 (? 开头,) 结尾的都称为非捕获组,在匹配完成后在内存中不保留匹配到的字符。

 

(1)(?:X)

    X 作为非捕获组
与捕获组 ( ) 的意思一样也是将其作为一组进行处理,与捕获组的区别在于不捕获匹配的文本,
仅仅作为分组。
比如:要匹配 123123 这个,就可以写为 (123)/1 使用反向引用,这时只能用捕获组,在匹配
123 后会保留在内存中,便于反向引用,而 (?:123) 在匹配完后则不会保留,区别仅在于此。

 

(2)(?idmsux-idmsux)  Nothing,但是将匹配标志i d m s u x on - off
用于标志匹配,比如:表达式 (?i)abc(?-i)def 这时,(?i) 打开不区分大小写开关,abc 匹配
不区分大小地进行匹配,(?-i) 关闭标志,恢复不区分大小写,这时的 def 只能匹配 def

(?idmsux-idmsux:X)  X,作为带有给定标志 i d m s u x on - off
与上面的类似,上面的表达式,可以改写成为:(?i:abc)def,或者 (?i)abc(?-i:def)

 

(3)(?=X) X,通过零宽度的正 lookahead 
    (?!X) X,通过零宽度的负 lookahead
(?=X) 表示当前位置(即字符的缝隙)后面允许出现的字符,比如:表示式 a(?=b),在字符串为
ab 时,可能匹配 a,后面的 (?=b) 表示,a 后面的缝隙,可以看作是零宽度。
(?!X) 表示当前位置后面不允许出现的字符

 

(4)(? <=X) X,通过零宽度的正 lookbehind 
    (? <!X) X,通过零宽度的负 lookbehind
这两个与上面两个类似,上面两个是向后看,这个是向前看
 (5)(?>X) X,作为独立的非捕获组
匹配成功不进行回溯,这个比较复杂,与侵占量词“+”可以通用,比如:/d++ 可以写为 (?>/d+)。

 

这里的侵占量词就是Possessive quantifiers,在API中的描述很好理解。

在这里我补充下 Reluctant quantifiers  Possessive quantifiers  Greedy quantifiers的区别,以便大家能更好的理解,看下面的这个例子:

  Pattern p = Pattern.compile(".{3,10}+[0-9]");
  String s = "aaaa5bbbb6";
  Matcher m = p.matcher(s);
  if(m.find())
   p(m.start() + "-" + m.end());
  else
   p("not match!");

注意正则表达式中的那个加号,他就代表Possessive quantifiers ,这里的输出结果是not match,而如果我们将“+”改为“?”,即".{3,10}[0-9]",那么输出的是

0-5,这就是Reluctant quantifiers  ,而我们最常用的Greedy quantifiers,

".{3,10}[0-9]",输出的是0-10.

 

为什么会这样呢? Possessive 英文意思占有的,在上面的程序中查找匹配的字符串时,注意这里是{3,10},所以直接取s的10个字符,即使后面还要有一个数字,他也不“吐出”一个字符,让匹配成功,所以就输出not match,而Reluctant quantifiers呢,他“勉强”的取字符,找到一个字符最少的匹配串,而 Greedy quantifiers它能够回溯 。

一个说明独立非捕获组的例子:
将一些多位的小数截短到三位小数:

/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+ 这样是一样的。

 

抱歉!评论已关闭.