输入字符串:
aaa---foo---ccc---ddd
aaa---bar---ccc---ddd
aaa---------ccc---ddd
正则表达式:aaa.*(foo|bar)?.*ccc.*(ddd)
此正则表达式在任何情况下都找不到第一组 (foo|bar)。对于捕获组 1,它始终返回 null
。
我的问题是为什么以及如何避免这种情况。
这是我的正则表达式的非常简单的示例,仅用于演示。如果我删除 ?
量词,但输入字符串可以根本没有这个组(aaa--------ccc---ddd
)并且我仍然需要确定它是foo
还是bar
还是null
。但组 1 始终为 null
。
包含此正则表达式和测试字符串的页面:http://fiddle.re/45c766
最佳答案
这就是它不起作用的原因:当模式中有 .*
时,匹配器的算法将尝试匹配尽可能多的字符,以使模式的其余部分正常工作。在这种情况下,如果它尝试从字符串的整个剩余部分作为 .*
开始并删除一个字符直到匹配,它会发现 (对于 "aaa---foo--- ccc---ddd"
) 可以让 .*
匹配 9 个字符;那么 (foo|bar)?
不匹配任何内容,这是可以的,因为它是可选的;接下来的 .*
匹配 0 个字符,然后模式的其余部分匹配。所以这就是它选择的那个。
将.*
更改为.*的原因?
:
aaa.*?(foo|bar)?.*?ccc.*(ddd)
不起作用的是匹配器反向执行相同的操作。它从 0 个字符匹配开始,然后确定是否可以使该模式起作用。当它尝试这样做时,它会发现它可以使 .*?
匹配 0 个字符;那么 (foo|bar)?
不匹配任何内容;那么第二个 .*?
匹配 9 个字符;那么模式的其余部分匹配ccc---ddd
。所以无论哪种方式,它都不会达到您想要的效果。
答案中有几个解决方案,都涉及前瞻。这是另一个解决方案:
aaa.*(foo|bar).*ccc.*(ddd)|aaa.*ccc.*(ddd)
这基本上按顺序检查两种模式;首先,它检查是否存在包含 foo|bar
的模式,如果不匹配,它将搜索另一种不包含 foo|bar
的可能性>。如果 foo|bar
存在的话,它总是会找到它。
不过,所有这些解决方案都涉及相当难以阅读的正则表达式。这就是我的编码方式:
Pattern pat1 = Pattern.compile("aaa(.*)ccc.*ddd");
Pattern pat2 = Pattern.compile("foo|bar");
Matcher m1 = pat1.matcher(source);
String foobar;
if (m1.matches()) {
Matcher m2 = pat2.matcher(m1.group(1));
if (m2.find()) {
foobar = m2.group(0);
} else {
foobar = null;
}
}
通常,尝试使用一个出色的正则表达式来解决问题会导致代码可读性较差(并且可能效率较低),而不仅仅是将问题分解为多个部分。
关于java - X?正则表达式量词无法按预期工作(我),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26346330/