飞利浦电视连接音响:java正则表达式 非捕获组,捕获组详解

来源:百度文库 编辑:偶看新闻 时间:2024/05/08 11:47:50
非捕获组(non-capturing)进行下总结。 (?:X) (?=X) (?<=X) (?!X) (?一、先从(?:)非捕获组说起。
下面由一个例子引出非捕获组。
有两个金额:8899¥ 和 6688$ 。显然,前一个是8899元的人民币,后一个是6688元的美元。我现在需要一个正则,要求提炼出它们的货币金额和货币种类。
正则可以这写:(\\d)+([¥$])$ (在java中测试,所以多了转义字符'\')
测试程序如下:copy
Pattern p = Pattern.compile("(\\d+)([¥$])$");
String str = "8899¥";
Matcher m = p.matcher(str);
if(m.matches()){
System.out.println("货币金额: " + m.group(1));
System.out.println("货币种类: " + m.group(2));
}
输出结果为:
货币金额: 8899
货币种类: ¥
OK,满足了要求。这里的正则分成了两个组,一个是(\\d+),一个是([¥$]),前一个组匹配货币金额,后一个组匹配货币种类。
现在,我需要这个正则可以匹配浮点数。如8899.56¥。我们都知道,现在少于一元钱基本上买不到东西了,所以我希望忽略小数部分,
正则还是提炼出 8899 和 ¥。
那么正则如下:
(\\d+)(\\.?)(\\d+)([¥$])$
这里用括号分了四组,所以要输出货币金额的整数部分和货币种类,要分别输了group(1),group(4)了。如果输出部分和正则是分开的,
我希望只修改正则而不去修改输出部分的代码,也就是还是用group(1),group(2)作为输出。由此可以引出非捕获组(?:)。
把前面的正则修改为:
(\\d+)(?:\\.?)(?:\\d+)([¥$])$
这样,还是用group(1),group(2)做为输出,同样输出了 8899 和 ¥
这个正则的中间两个组用到的就是非捕获组(?:),它可以理解为只分组而不捕获。
二、(?=)和(?<=)
有的资料把它们叫做肯定式向前查找和肯定式向后查找;
有的资料也叫做肯定顺序环视和肯定逆序环视。
1、姑且不理它们的名称,看下面的例子:
Pattern p = Pattern.compile("[0-9a-z]{2}(?=aa)");
String str = "12332aa438aaf";
Matcher m = p.matcher(str);
while(m.find()){
System.out.println(m.group());
}
这段程序输出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,不满足正则的匹配条件,查找结束。
我们再延伸一下,刚说的情况的是(?=)放在捕获的字符串后面,它如果放在前面又是什么结果呢?
例子换成:copy
Pattern p = Pattern.compile("(?=hopeful)hope");
String str = "hopeful";
Matcher m = p.matcher(str);
while(m.find()){
System.out.println(m.group());
}
它的输出是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正则表达式 非捕获组
在 java api 文档中的正则表达式关于特殊构造(非捕获组)的说明看不懂。例如:(?:X) X,作为非捕获组
(?idmsux-idmsux) Nothing,但是将匹配标志由 on 转为 off
(?idmsux-idmsux:X) X,作为带有给定标志 on - off 的非捕获组
(?=X) X,通过零宽度的正 lookahead
(?!X) X,通过零宽度的负 lookahead
(?<=X) X,通过零宽度的正 lookbehind
(?(?>X) X,作为独立的非捕获组
这些字都说的很抽象。不懂……。还是搜索下去。找到 火龙果 的解释如下:
以 (? 开头,) 结尾的都称为非捕获组,在匹配完成后在内存中不保留匹配到的字符。
非捕获组的应用比较复杂,这里只能简单地说一下它们的意思。
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
3、(?idmsux-idmsux:X) X,作为带有给定标志 i d m s u x on - off
与上面的类似,上面的表达式,可以改写成为:(?i:abc)def,或者 (?i)abc(?-i:def)
4、(?=X) X,通过零宽度的正 lookahead
5、(?!X) X,通过零宽度的负 lookahead
(?=X) 表示当前位置(即字符的缝隙)后面允许出现的字符,比如:表示式 a(?=b),在字符串为
ab 时,可能匹配 a,后面的 (?=b) 表示,a 后面的缝隙,可以看作是零宽度。
(?!X) 表示当前位置后面不允许出现的字符
6、(? <=X) X,通过零宽度的正 lookbehind
7、(? 这两个与上面两个类似,上面两个是向后看,这个是向前看
8、(?>X) X,作为独立的非捕获组
匹配成功不进行回溯,这个比较复杂,也侵占量词“+”可以通用,比如:\d++ 可以写为 (?>\d+)。
我认为,第1、2、3点比较好理解,4、5、6、7看类懂,还是用示例来说明:从“aacabab”找a,且后面只允许出现b。代码如下:
Pattern p = Pattern.compile("a(?=b)");
Matcher m = p.matcher("aacabab");
while(m.find()) {
System.out.println(m.group()+", start="+m.start()+", end="+m.end());
}
运行结果:
a, start=3, end=4
a, start=5, end=6
个人理解:在(?=b)这个“式”后面允许出现b,且这个“式”不占正则表达式位置(所谓0宽度),lookahead 的意思是b字符的前面,
它前面紧接着是a,也就是a后面出现b。
8比较难理解
其中说的示例:来看 /\b(integer|insert|in)\b/ 匹配 integers 过程,第一个,当integer\b匹配到s时失败,然后字符串(integers)会回溯到i,
再接着第二个(insert)去匹配。而把模式写成 /\b(?>integer|insert|in)\b/ 在刚才的第一个匹配失败,字符串(integers)不会回溯了,也不会
有第二个去匹配了,所有速度会快一点点。
但是写 (?>X) 这种式子时要注意,是从左到右看的。/\b(?>integer|insert|in)\b/ ,与 /\b(?>in|integer|insert)\b/ 去匹配 insert,结果会不一样,
前者可以匹配到,后者不能,什么原因自己分析下。一但匹配失败就会跳过,所以应该长的写在表达式前面。
复习下Java正则表达式的捕获组和非捕获组
2008-04-20 19:44
比如有下面一段代码:
aaa
bbb
ccc
ddd
eee
fff
上面的代码意思是 不一定有,而且color的值也可能不一样
我现在想得到
aaa
bbb
ccc
ddd
eee
fff
这个正则表达式该怎么写?谢谢 】
我的解答:(未使用非捕获组,借用了String方法的replaceAll)
package test1;
import java.util.regex.*;
public class Test6
{
public static void main(String[] args)
{
String s=" aaa "
+" bbb "
+"ccc "
+" ddd "
+" eee "
+"fff ";
String regex="(.*?)";
Pattern pt=Pattern.compile(regex);
Matcher mt=pt.matcher(s);
while(mt.find())
{
System.out.println(mt.group(1).replaceAll("|", "").trim());
}
}
}
解答二:使用非捕获组
package test1;
import java.util.regex.*;

public class Test6 ...{
public static void main(String[] args) ...{
String str = " aaa " +
" bbb " +
"ccc " +
" ddd " +
" eee " +
"fff ";
String regex = "(?:/s*]*>)?(.*?)(?:/s*)?";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while(matcher.find()) ...{
System.out.println(matcher.group(1));
}
}
}

总结:什么是非捕获组、什么是捕获组. ----------引自帮助文档
组和捕获
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C))) 中,存在四个这样的组:
1 ((A)(B(C)))
2 /A
3 (B(C))
4 (C)
组零始终代表整个表达式。
之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用在表达式中使用,
也可以在匹配操作完成后从匹配器获取。
与组关联的捕获输入始终是与组最近匹配的子序列。如果由于量化的缘故再次计算了组,则在第二次计算失败时将保留其以前捕获的值(如果有的话)
例如,将字符串 "aba" 与表达式 (a(b)?)+ 相匹配,会将第二组设置为 "b"。在每个匹配的开头,所有捕获的输入都会被丢弃。
以 (?) 开头的组是纯的非捕获 组,它不捕获文本,也不针对组合计进行计数。
要了解非捕获组就要先了解捕获组,之后再了解为什么会有非捕获组的出现 。
简单点说,捕获组就是把(Expression)中匹配到的内容保存到一个按“(”出现的顺序编号的组里,以供后续引用,引用的方式有反向引用,或是RegExp.$number等方式,不同的语言,支持的引用方式不同 。
只要使用了(),默认为使用了捕获组,而这就带来一个问题,有些场景不得不使用(),但又不关心它匹配到的内容,比如写一个匹配24小时制HH:mm:ss的时间的正则如下:
([01][0-9]|2[0-3])(:([0-5][0-9])){2}
通常关心的只是整体的时间,并不关心局部的内容,这样就产生了一种副作用,将不关心的内容单独保存到内存中,只会浪费资源,降低效率 。
非捕获组就是为了抵消这一副作用来产生的,非捕获组只参与匹配,但不会把匹配到的内容捕获到组里。
所以非捕获组根本就不参与编号,也就无从谈起它对应哪个$number
在取不存在的编号的捕获组时,有些语言会返回空字符串,有些语言会报异常
(\d+\.\d+(?:\.\d+)?)中,整体是一个捕获组,按“(”出现的顺序,编号为1,(?:\.\d+)虽然是非捕获组,也是要参与匹配的,只是不将匹配结果单独保存到组里而已。
正则表达式学习笔记(5):分组、捕获性分组和非捕获性分组
问题提出:将格式为:"25/10/2009"的时间转换为"2009-10-25"的格式
问题分析:
1、年份2009、月份10、日25应该做为一个独立的单位,这样我们可以直接知道什么地方匹配的是年、月还是日
2、可以用\d\d\d\d来匹配年份,用\d\d来匹配月份,用\d\d来匹配日
3、应该可以有一个地方存储匹配过的结果,以便于转换
解决问题所必须知道的:
1、分组就是将若干单位(可以是字符,正则表达式等等)组织在一起,成为一个独立的单元,该单位可以跟独立的字符一样,受量词的控制,分组使用()表示
2、分组分为捕获性分组和非捕获性分组,简单的说捕获性分组就是捕获分组所匹配的内容暂且存储在某个地方,以便下次使用,捕获性分组以(...)表示,有些地方将取得捕获性分组
所匹配结果的过程称之为"反向引用",非捕获性分组不捕获分组所匹配的内容,当然也就得不到匹配的结果,非捕获性分组以(?:...)表示,在一些只需要分组匹配但是并不需要得到各个
分组匹配的结果时,使用非捕获性分组可以提高匹配速度。
3、在JavaScript和Java中,捕获性分组所匹配的内容都是以$1,$2,$3...的格式保存的!
解决问题:
1、初步表达式:(\d\d)/(\d\d)/(\d\d\d\d),第一个分组(\d\d)表示日,第二个分组(\d\d)表示月,最后一个分组(\d\d\d\d)表示年
2、取得匹配后的结果:
在JavaScript中,分组匹配的结果是存储在RegExp中的,可以通过RegExp.$1,RegExp.$2...来取得相应的分组匹配结果

    
    
在Java中,可以通过Matcher的group(int)方法来取得:
String now = "25/10/2009";
        Matcher matcher = Pattern.compile("(\\d\\d)/(\\d\\d)/(\\d\\d\\d\\d)").matcher(now);
        if (matcher.find()) {
            System.out.println(matcher.group(1));
            System.out.println(matcher.group(2));
            System.out.println(matcher.group(3));
        }
注意:在String的replace()方法中,也可以直接以$1,$2...的格式来取得匹配结果
3、转换:
JavaScript:
    
    
    
Java:
String now = "25/10/2009";
        Matcher matcher = Pattern.compile("(\\d\\d)/(\\d\\d)/(\\d\\d\\d\\d)").matcher(now);
        if (matcher.find()) {
            System.out.println(matcher.group(3) + "-" + matcher.group(2) + "-" + matcher.group(1));
        }
4、一个简单的写法:
JavaScript:
    
    
    
Java:
String now = "25/10/2009";
        System.out.println(now.replaceAll("(\\d\\d)/(\\d\\d)/(\\d\\d\\d\\d)", "$3-$2-$1"));
5、分组结果量词使用
上文提到,分组是可以结合量词使用的,所以上面的程序也可以这样来写:
JavaScript;

    
    
Java:

      String now = "25/10/2009";
        System.out.println(now.replaceAll("(\\d{2})/(\\d{2})/(\\d{4})", "$3-$2-$1"));