Java 正则表达式全攻略(七)
原子组与防治回溯
原子组在 Java 里也被称为“独立非捕获组”,.Net方面的说法称为“贪婪子表达式”,不过我还是觉得称为原子组更合适些。它的语法为 (?>X) ,具体来说,使用原子组匹配和正常的匹配没有差别,只是在匹配到原子组结束时(即闭括号之后),原子组中的所有可供回溯的备用状态都会被丢弃。也就是说, 在原子组匹配结束时,原子组的匹配内容只能整体保留或丢弃,回溯始终不能使用这些已匹配过的内容。
还是以一个实例来说明原子组会更为清晰。例如使用 a(bc|b)c (捕获组)这一表达式你可以匹配 abcc 和 abc,但如果使用 a(?>bc|b)c (原子组)这表达式,你只能匹配 abcc 而不能匹配 abc。
在匹配 abc 时,两个表达式都会先匹配到 a ,然后在成功匹配到 bc ,再接着尝试匹配 c ,这时就会出现匹配失败。一般捕获组的表达式,这时会回溯到进入组前的状态,再匹配另一条路径,先匹配 b 成功,再重新尝试匹配 c 也成功,这是 abc 的匹配就成功了。而使用原子组的表达式,由于在 bc 匹配成功,结束组时就备份状态丢弃了,所以当匹配 c 失败时,就是直接的失败,表达式引擎并没有其它状态可供回溯。
使用原子组可以有效地防止不必要的回溯,因此可以大大提高表达式的性能。
匹配模式
在 Java 内我们除了可以在创建 Pattern 对象实例时,声明匹配模式外,还可以直接在表达式内声明表达式的匹配模式。下面为可用匹配标识的具体含义:
i | CASE_INSENSITIVE | 启用不区分大小写的匹配 |
d | UNIX_LINES | 启用 Unix 行模式。在此模式中,.、^ 和 $ 的行为中仅识别 '\n' 行结束符。 |
m | MULTILINE | 启用多行模式。 |
s | DOTALL | 启用 dotall 模式。在 dotall 模式中,表达式 . 可以匹配任何字符,包括行结束符。默认情况下,此表达式不匹配行结束符。 |
u | UNICODE_CASE | 启用 Unicode 感知的大小写折叠。 |
x | COMMENTS | 模式中允许空白和注释。 |
以下面的代码来说:
1: String regex = "(?i)ab(?-i)cd" ;
2: assertThat(Pattern.matches(regex, "ABcd" ), is(true));
3: assertThat(Pattern.matches(regex, "abCD" ), is(false));
(?i) 开启了不区分大小写,所以 ABcd 可以被匹配;而 (?-i) 则为关闭不区分大小写,所以 abCD 无法被匹配。对上面的表达式,Java 还支持另一种写法 (?i:ab)(?-i:cd) 。