这几天看了下正则表达式,对非捕获组(non-capturing)进行下总结。
主要总结 1个 + 2组  一共5个。
(?:X) (?=X) (?<=X) (?!X) (?<!X)

一、先从(?:)非捕获组说起。
下面由一个例子引出非捕获组。

有两个金额:8899¥ 和 6688$ 。显然,前一个是8899元的人民币,后一个是6688元的美元。我现在需要一个正则,要求提炼出它们的货币金额和货币种类。正则可以这写:(\\d)+([¥$])$  (在java中测试,所以多了转义字符'\')
测试程序如下:

  1. Pattern p = Pattern.compile("(\\d+)([¥$])$");
  2. String str = "8899¥";
  3. Matcher m = p.matcher(str);
  4. if(m.matches()){
  5. System.out.println("货币金额: " + m.group(1));
  6. System.out.println("货币种类: " + m.group(2));
  7. }

输出结果为:
货币金额: 8899
货币种类: ¥

OK,满足了要求。这里的正则分成了两个组,一个是(\\d+),一个是([¥$]),前一个组匹配货币金额,后一个组匹配货币种类。

现在,我需要这个正则可以匹配浮点数。如8899.56¥。我们都知道,现在少于一元钱基本上买不到东西了,所以我希望忽略小数部分,正则还是提炼出 8899 和 ¥。
那么正则如下:
[code="java"](\\d+)(\\.?)(\\d+)([¥$])$[/code]
这里用括号分了四组,所以要输出货币金额的整数部分和货币种类,要分别输了group(1),group(4)了。如果输出部分和正则是分开的,我希望只修改正则而不去修改输出部分的代码,也就是还是用group(1),group(2)作为输出。由此可以引出非捕获组(?:)。
把前面的正则修改为:
[code="java"](\\d+)(?:\\.?)(?:\\d+)([¥$])$[/code]
这样,还是用group(1),group(2)做为输出,同样输出了 8899 和 ¥
这个正则的中间两个组用到的就是非捕获组(?:),它可以理解为只分组而不捕获。

二、(?=)和(?<=)
有的资料把它们叫做肯定式向前查找和肯定式向后查找;
有的资料也叫做肯定顺序环视和肯定逆序环视。

1、姑且不理它们的名称,看下面的例子:

  1. Pattern p = Pattern.compile("[0-9a-z]{2}(?=aa)");
  2. String str = "12332aa438aaf";
  3. Matcher m = p.matcher(str);
  4. while(m.find()){
  5. System.out.println(m.group());
  6. }

这段程序输出32 38
这个正则的意思是:匹配这么一个字符串,它要满足:是两位字符(数字,或字母),且[color=red]后面[/color]紧跟着两个a。
分析一下:
32aa  这个子串 满足这个条件,所以可以匹配到,又因为 (?=) 的部分是不捕获的,所以输出的只是 32,不包括aa。同理 38aa 也匹配这个正则,而输出仅是 38。

再深入看一下:
当str第一次匹配成功输出 32 后,程序要继续向后查找是否还有匹配的其它子串。那么这时应该从 32aa 的后一位开始向后查找,还是从 32 的后一位呢?也就是从索引 5 开始还是从 7 开始呢?有人可能想到是从 32aa 的下一位开始往后找,因为 32aa 匹配了正则,所以下一位当然是它的后面也就是从 4 开始。但实际上是从 32 的后一位也就是第一个 a 开始往后找。原因还是 (?=) 是非捕获的。
查阅API文档是这么注释的:

(?=X) X, via zero-width positive lookahead

可见zero-width(零宽度)说的就是这个意思。

现在,把字符串写的更有意思些:str = "aaaaaaaa";
看一下它的输出: aa aa aa
分析一下:
这个字符串一共有8个a。
第一次匹配比较容易找到,那就是前四个:aaaa ,当然第三和第四个 a 是不捕获的,所以输出是第一和第二个a;
接着继续查找,这时是从第三个a开始,三到六,这4个a区配到了,所以输出第三和第四个a;
接着继续查找,这时是从第五个a开始,五到八,这4个a区配到了,所以输出第五和第六个a;
接着往后查找,这时是从第七个a开始,显然,第七和第八个a,不满足正则的匹配条件,查找结束。
我们再延伸一下,刚说的情况的是(?=)放在捕获的字符串后面,它如果放在前面又是什么结果呢?
例子换成:

  1. Pattern p = Pattern.compile("(?=hopeful)hope");
  2. String str = "hopeful";
  3. Matcher m = p.matcher(str);
  4. while(m.find()){
  5. System.out.println(m.group());
  6. }

它的输出是hope。
正则的意思是:是否能匹配hopeful,如果能,则捕获hopeful中的hope。当然继续向后查找匹配的子串,是从f开始。
比较一下可以看出,(?=hopeful)hope 和 hope(?=ful),两个正则的效果其实是一样的。

2、下面说一下 (?<=)
把正则改一下,
    Pattern p = Pattern.compile("(?<=aa)[0-9a-z]{2}");
字符串还是str = "12332aa438aaf";
它的输出:43。

这个正则的意思是:匹配这么一个字符串,它要满足:是两位字符(数字或字母),且[color=red]前面[/color]紧跟的是两个字母 a 。

同样,深入一下,把str换成str = "aaaaaaaa";看一下输出是什么,同样也是:aa aa aa
分析一下:
第一次匹配不用说,是前四个a,输出的是第三和第四个a;
继续向后查找,从第五个a开始,程序发现,第五个和第六个a满足,因为是两位字符,且满足前面紧跟着两个a(第三和第四个a)。所以匹配成功,输出第五个和第六个a;
继续向后查找,从第七个a开始,程序发现,第七个和第八个a满足,因为是两位字符,且满足前面紧跟着两个a(第五和第六个a)。所以匹配成功,输出第七和第八个a。查找结束。

三、(?!)和(?<!)
从外观上看,和前面一组很相似,区别就是把 ‘=’ 换成了 ‘!’
那么意义刚好也是相反的。
[0-9a-z]{2}(?!aa)    意思是:匹配两个字符,且后面紧跟着的不是aa
(?<=aa)[0-9a-z]{2}  意思是:匹配两个字符,且前面紧跟着的不是aa
用法和前面讲的差不多,这里不再详述。

java正则表达式 非捕获组详解的更多相关文章

  1. Java 正则表达式之捕获组

    Java 正则表达式之捕获组 1. Java 正则表达式基础 2. Java 正则表达式之捕获组 一.概述 1.1 什么是捕获组 捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显 ...

  2. Java正则表达式的使用和详解(下)

    1.常用正则表达式 规则 正则表达式语法   一个或多个汉字 ^[\u0391-\uFFE5]+$  邮政编码 ^[1-9]\d{5}$ QQ号码 ^[1-9]\d{4,10}$  邮箱 ^[a-zA ...

  3. Java正则表达式的使用和详解(上)

    1.匹配验证-验证Email是否正确 public static void main(String[] args) { // 要验证的字符串 String str = "service@xs ...

  4. JAVA正则表达式-捕获组与非捕获组

    Java捕获组与非捕获组的问题 先看例子: import java.util.regex.Matcher; import java.util.regex.Pattern; public class P ...

  5. 正则表达式的捕获组(Java)

    捕获组分类 普通捕获组(Expression) 命名捕获组(?<name>Expression) 普通捕获组 从正则表达式左侧开始,每出现一个左括号“(”记做一个分组,分组编号从1开始.0 ...

  6. 正则表达式的捕获组(capture group)在Java中的使用

    原文:http://blog.csdn.net/just4you/article/details/70767928 ------------------------------------------ ...

  7. php 正则表达式捕获组与非捕获组

    熟练掌握正则表达式是每个程序员的基础要求,对于每个初学者来说会被正则表达式一连串字符弄得头晕眼花.博主便会如此,一直对正则表达式有种莫名的恐惧.近来看到另一位博友写的 <php正则表达式> ...

  8. “全栈2019”Java多线程第二十八章:公平锁与非公平锁详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  9. Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO

    Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO Java 非阻塞 IO 和异步 IO 转自https://www.javadoop.com/post/nio-and-aio 本系 ...

随机推荐

  1. POJ 1741 树上的点分治

    题目大意: 找到树上点对间距离不大于K的点对数 这是一道简单的练习点分治的题,注意的是为了防止点分治时出现最后分治出来一颗子树为一条直线,所以用递归的方法求出最合适的root点 #include &l ...

  2. 父元素与子元素之间的margin-top问题(css hack)(转载)

    情况: 父元素的盒子包含一个子元素盒子,给子元素盒子一个垂直外边距margin-top,父元素盒子也会往下走margin-top的值,而子元素和父元素的边距则没有发生变化. 解决方法: 1.修改父元素 ...

  3. 在shell脚本中进行条件控制以及使用循环

    转载请标明:http://www.cnblogs.com/winifred-tang94/ if条件语句语法: if [ 条件表达式 ] then 代码 else 代码 fi 注意:在上面的if条件语 ...

  4. 网络安全之PHP安全编程建议

    要提供互联网服务,当你在开发代码的时候必须时刻保持安全意识.可能大部分 PHP 脚本都对安全问题都不在意,这很大程度上是因为有大量的 无经验程序员 在使用这门语言.但是,没有理由让你因为对你的代码的不 ...

  5. innerHTML..innerText..textContent

    /* * innerText和textContent 都是设置文字内容的,如果设置的内容当中有标签,也会直接的以文本的方式显示(标签的<>都会按照转义的方式进行解析) * innerTex ...

  6. Javascript 基础--数组

    一.一维数组 1.一维数组 var weights = [3,5,1,3.4,2,50]; var all_weight=0; var avg_weight=0; for(var i=0;i<w ...

  7. 收藏的博客--Ogre

    1.Ogre一些东西和流程深入讲解  http://blog.csdn.net/yanonsoftware/article/category/226048 2.Ogre天龙八部分析 http://bl ...

  8. Multiple dex files define

    Multiple dex files define 在项目中,有一个类的包名和引用的jar包中的类和包名一致

  9. IDisposable接口

    C#中IDisposable接口的主要用途是释放非托管资源.当不再使用托管对象时,垃圾回收器会自动释放分配给该对象的内存.但无法预测进行垃圾回收的时间.另外,垃圾回收器对窗口句柄或打开的文件和流等非托 ...

  10. JS-OC通信之Cordova简介

    Cordova 是一个可以让 JS 与原生代码(包括 Android 的 java,iOS 的 Objective-C 等)互相通信的一个库,并且提供了一系列的插件类,比如 JS 直接操作本地数据库的 ...