正则表达式贪婪量词

4

我正在回答这个问题这里是我的答案直接链接。

你会注意到我使用了这种模式:

(\\?)?&?(TXT\\{[^}]++})(&)?

在以下代码中(添加了一些与我的问题相关的调试信息):
public static void main(final String[] args) throws Exception {
    final String[] loginURLs = {
        "http://ip:port/path?username=abcd&location={LOCATION}&TXT{UE-IP,UE-Username,UE-Password}&password={PASS}",
        "http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}&TXT{UE-IP,UE-Username,UE-Password}",
        "http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}&username=abcd&location={LOCATION}&password={PASS}",
        "http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}",
        "http://ip:port/path?username=abcd&password={PASS}"};
    final Pattern patt = Pattern.compile("(\\?)?&?(TXT\\{[^}]++})(&)?");
    for (final String loginURL : loginURLs) {
        System.out.printf("%1$-10s %2$s%n", "Processing", loginURL);
        final StringBuffer sb = new StringBuffer();
        final Matcher matcher = patt.matcher(loginURL);
        while (matcher.find()) {
            final String found = matcher.group(2);
            System.out.printf("%1$-10s 1:%2$s,3:%3$s%n", "Groups", matcher.group(1), matcher.group(3));
            System.out.printf("%1$-10s %2$s%n", "Found", found);
            if (matcher.group(1) != null && matcher.group(3) != null) {
                matcher.appendReplacement(sb, "$1");
            } else {
                matcher.appendReplacement(sb, "$3");
            }
        }
        matcher.appendTail(sb);
        System.out.printf("%1$-10s %2$s%n%n", "Processed", sb.toString());
    }
}

其中的输出为:

Processing http://ip:port/path?username=abcd&location={LOCATION}&TXT{UE-IP,UE-Username,UE-Password}&password={PASS}
Groups     1:null,3:&
Found      TXT{UE-IP,UE-Username,UE-Password}
Processed  http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}

Processing http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}&TXT{UE-IP,UE-Username,UE-Password}
Groups     1:null,3:null
Found      TXT{UE-IP,UE-Username,UE-Password}
Processed  http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}

Processing http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}&username=abcd&location={LOCATION}&password={PASS}
Groups     1:?,3:&
Found      TXT{UE-IP,UE-Username,UE-Password}
Processed  http://ip:port/path?username=abcd&location={LOCATION}&password={PASS}

Processing http://ip:port/path?TXT{UE-IP,UE-Username,UE-Password}
Groups     1:?,3:null
Found      TXT{UE-IP,UE-Username,UE-Password}
Processed  http://ip:port/path

Processing http://ip:port/path?username=abcd&password={PASS}
Processed  http://ip:port/path?username=abcd&password={PASS}

这很完美。

现在,我的问题是

当我将第一个匹配组 (\\?)? 改为使用占有量词,例如 (\\?)?+ 时,第一个 项目的输出结果变成:

Processing http://ip:port/path?username=abcd&location={LOCATION}&TXT{UE-IP,UE-Username,UE-Password}&password={PASS}
Groups     1:?,3:&
Found      TXT{UE-IP,UE-Username,UE-Password}
Processed  http://ip:port/path?username=abcd&location={LOCATION}?password={PASS}

我真的想不出来第一个匹配组中的问号是从哪里来的。

我没有看到这个模式正确匹配所需字符串并在第一组中捕获问号的方法。

难道我错过了什么显而易见的东西吗?

如果有关系的话,我正在运行带有以下内容的OS X Mavericks:

java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)

2
这不是这个bug吗? - raina77ow
@raina77ow 看起来对我来说也是这样。自从7u9以来它还没有被修复,有点荒谬!很高兴知道我没有疯掉。如果你把它发表为答案,我会接受的。 - Boris the Spider
我确认,这确实是同样的 bug。然而,它有时可能是一种有用的行为。 - Casimir et Hippolyte
@CasimiretHippolyte 哈!这让我想起了 这个xkcd ... - Boris the Spider
@BoristheSpider:通过标志激活/停用某些行为将会很酷:Pattern.CRAZYMOD - Casimir et Hippolyte
1个回答

3
我猜这与占有量词的工作方式有关。首先它们像贪婪量词一样工作。也就是说,它们会尽可能匹配更多内容。但与贪婪量词不同的是,一旦它们匹配了某些内容,在回溯后它们不会放弃这个匹配。
因此,以你的正则表达式为例:
"(\\?)?+&?(TXT\\{[^}]++})(&)?"

它首先找到username之前的?,匹配并将其存储在第一组中。然后它发现下一个字符&不匹配username中的u。所以它回溯,并停在?。由于它被匹配为贪婪量词,因此他们不会失去匹配。
现在,它继续进行。此时,第一组仍然包含?。现在它匹配以下部分:
&TXT{UE-IP,UE-Username,UE-Password}&

由于?是可选的,所以它不会被匹配。但是它不会替换第1组中的任何内容。

这意味着,您将获取第1组中第一次匹配的?


这似乎是Java正则表达式引擎中的错误,因为在Perl中,该组被视为未定义。这是样例


这是占有量词的期望行为吗?正如您指出的那样,?来自完全不同的、失败的匹配。在匹配尝试失败后,引擎的状态不应该被清除吗? - Boris the Spider
1
我同意@BoristheSpider的观点,这是一个程序错误;由于一开始就没有匹配到文本,所以捕获组不应该保留文本。 - fge
@BoristheSpider 嗯,我在 Perl 上测试了一下,结果和预期一样。这肯定是 Java 正则表达式引擎的一个 bug。 - Rohit Jain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接