正则表达式 特殊构造、非捕获组、lookahead 与 lookbehind

组和捕获

捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C))) 中,存在四个这样的组:

1       ((A)(B(C)))
2       (A)
3       (B(C))
4       (C) 
每一个“()”是一个捕获组,组零($0)始终代表整个表达式
Pattern pattern = Pattern.compile(reg);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
    for (int i = 0; i <= matcher.groupCount(); i++) {
          matcher.group(i);//读捕获组
    }
}
使用正则表达式可以使用“$数字”来表示捕获组
测试
如:"abc 123".replaceAll("(\\w+)\\s+(\\d+)","$2-$1");
输出:123-abc

特殊构造(非捕获)

非捕获,即不会当做一个捕获组
(?:X)   X,作为非捕获组
(?idmsux-idmsux)    Nothing,但是将匹配标志由 on 转为 off
(?idmsux-idmsux:X)      X,作为带有给定标志 on - off 的非捕获组
(?=X)   X,通过零宽度的正 lookahead
(?!X)   X,通过零宽度的负 lookahead
(?<=X)  X,通过零宽度的正 lookbehind
(?X)   X,作为独立的非捕获组

(?:X) X,作为非捕获组

//(?:a)(\\d{3})代表着这样的功能:
//查找给出的字符串中符合a后面有三个数字的这样的字符串,当然取得的子串不包括"a"
 Pattern pattern = Pattern.compile("(?:a)(\\d{3})");
 Matcher matcher = pattern.matcher("a444b666c888a999");
 while (matcher.find()) {
     for (int i = 0; i <= matcher.groupCount(); i++) {
         System.out.println("group" + i + ": " + matcher.group(i));
     }
 }
 (?:a),不会当作一个捕获组,输出结果:
    group0: a444
    group1: 444
    group0: a999
    group1: 999

(?idmsux-idmsux)

(?i): 默认情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集中的字符。可以通过指定 UNICODE_CASE 标志连同此标志来启用 Unicode 感知的、不区分大小写的匹配。

(?d): 启用 Unix 行模式。在此模式中,.、^ 和 $ 的行为中仅识别 ‘\n’ 行结束符。

(?m): 启用多行模式。
在多行模式中,表达式 ^ 和 $ 仅分别在行结束符前后匹配,或者在输入序列的结尾处匹配。默认情况下,这些表达式仅在整个输入序列的开头和结尾处匹配。

(?s): 启用 dotall 模式。
在 dotall 模式中,表达式 . 可以匹配任何字符,包括行结束符。默认情况下,此表达式不匹配行结束符。

(?u): 启用 Unicode 感知的大小写折叠。
指定此标志后,由 CASE_INSENSITIVE 标志启用时,不区分大小写的匹配将以符合 Unicode Standard 的方式完成。默认情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集中的字符。

(?x) : 模式中允许空白和注释。
此模式将忽略空白和在结束行之前以 # 开头的嵌入式注释。

忽略大小写示例

    Pattern pattern = Pattern.compile("(?i)[A-Z]\\d{3}");
    Matcher matcher = pattern.matcher("a444b666c888a999");
    while (matcher.find()) {
        for (int i = 0; i <= matcher.groupCount(); i++) {
            System.out.println("group" + i + ": " + matcher.group(i));
        }
    }

lookahead与lookbehind

表达式前后检查关系;
lookahea在简单粗暴的理解就是:(?=X)与(?!X)前面需要有一个表达式才能生效
lookbehind就是:(?<=X)与(?

(?=X) X,通过零宽度的正 lookahead

Pattern pattern = Pattern.compile("\\d{3}(?=a)");
Matcher matcher = pattern.matcher("a444b666c888a999");
while (matcher.find()) {
    System.out.println(matcher.group());
}
\\d{3}(?=a)查找给出的字符串中符合a前面有三个数字的这样的字符串,只取出3个数字,不包括a
输出结果:888
如果把表达式 \\d{3}(?=a)换成(?=a)\\d{3},无法匹配到任务内容

(?!X) X,通过零宽度的正 lookahead

Pattern pattern = Pattern.compile("\\d{3}(?!a)");
Matcher matcher = pattern.matcher("a444b666c888a999");
while (matcher.find()) {
    System.out.println(matcher.group());
}
\\d{3}(?!a)代表着连续三个数字的后面出现的字符不是a的匹配,
输出结果:
444
666
999

(?<=X) X,通过零宽度的正 lookbehind

Pattern pattern = Pattern.compile("(?<=a)\\d{3}");
Matcher matcher = pattern.matcher("a444b666c888a999");
while (matcher.find()) {
    System.out.println(matcher.group());
}
(?<=a)\\d{3}代表着a后面是连续三个数字出现的字符的匹配,
输出结果:
444
999

(?<!X),X 通过零宽度的正 lookbehind

Pattern pattern = Pattern.compile("(?

总结

behindhead 是说“目标/待查找字符串”的位置,正/负是条件字符串出现与否。 (?<=X)与(? 和 (?=X)与(?!X)  
刚好成对,一个在前一个在后,一个包含一个不包含。

(?:X)的作用是取消分组,但grup(0)时仍然包含该组的内容。
非捕获组,其实这个对结果不影响的,只是说你这个括号不是分组,只是为了吧一段规则包含起来。
因为保存分组需要时间和内存。使用非捕获组可以提升性能。

参考资料

https://www.regular-expressions.info/lookaround.html
http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/util/regex/Pattern.html#cg
https://blog.csdn.net/lsp1991/article/details/47974785

你可能感兴趣的:(java,正则表达式,java)