java $ ¥_java正则表达式非捕获组详解

非捕获组(non-capturing): (?:X) (?=X) (?<=X) (?!X) (?

一、先从(?:)非捕获组说起

下面由一个例子引出非捕获组。

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

测试程序如下:

package test;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

public class TestReg {

public static void main(String[] args) {

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,不满足正则的匹配条件,查找结束。

我们再延伸一下,刚说的情况的是 (?=)放在捕获的字符串后面,它如果放在前面又是什么结果呢?

例子换成:

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正则表达式非捕获组

在 javaapi 文档中的正则表达式关于特殊构造(非捕获组) 的说明看不懂。例如:(?: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 su x on - off

用于标志匹配,比如:表达式 (?i)abc(?-i)def这时,(?i) 打开不区分大小写开关,abc 匹配

不区分大小地进行匹配,(?-i) 关闭标志,恢复不区分大小写,这时的 def只能匹配 def

3、(?idmsux-idmsux:X)X,作为带有给定标志 i d ms 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正则表达式的捕获组和非捕获组

比如有下面一段代码:

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 "

+"

color="22">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"。在每个匹配的开头,所有捕获的输入都会被丢弃。

非捕获组

以 (?) 开头的组是纯的非捕获组,它不捕获文本,也不针对组合计进行计数。

你可能感兴趣的:(java,$,¥)