首页 > 解决方案 > 如何使捕获组可选?

问题描述

输入

example("This is tes't")

example('This is the tes\"t')

输出应该是

This is tes't

This is the tes"t

代码

 String text = "example(\"This is tes't\")";
//String text = "$.i18nMessage('This is the tes\"t\')";
final String quoteRegex = "example.*?(\".*?\")?('.*?')?";
        Matcher matcher0 = Pattern.compile(quoteRegex).matcher(text);
        while (matcher0.find()) {
            System.out.println(matcher0.group(1));
            System.out.println(matcher0.group(2));

        }

我看到输出为

null
null

虽然当我使用正则表达式时example.*?(\".*?\")它返回This is tes't并且当我使用example.*?('.*?') 它时返回 This is the tes"t但是当我将两者结合使用example.*?(\".*?\")?('.*?')?它返回 null 。为什么 ?

标签: javaregex

解决方案


.*?(\".*?\")?('.*?')?正则表达式末尾的子模式序列可以匹配一个空字符串(所有 3 个部分都用匹配0 个或更多字符的*/量化)。*?在匹配之后example.*?首先跳过,并且仅在后续子模式不匹配时才扩展。但是,它们都匹配之前的空字符串(,因此,您只有examplein matcher0.group(0)

使用使第 1 组成为强制性的替代方案(demo):

Pattern.compile("example.*?(\".*?\"|'.*?')"

或者带有缓和的贪婪令牌(demo)的变体,可以摆脱交替:

Pattern.compile("example.*?(([\"'])(?:(?!\\2).)*\\2)"

或者,更好的是,支持转义序列(另一个演示):

Pattern.compile("example.*?(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*')"

在所有 3 个示例中,您只需要访问第 1 组。如果只能(exampleand "or之间存在',则应替换.*?为,\(因为它会使匹配更安全。虽然,使用正则表达式匹配字符串文字(至少使用一个正则表达式)永远不会太安全。


推荐阅读