Java正则表达式交替运算符“|”行为似乎破裂了

时间:2010-12-23 02:05:10

标签: java regex regex-alternation

尝试为罗马数字编写正则表达式匹配器。在sed中(我认为它被认为是正则表达式的“标准”),如果你有多个由交替运算符分隔的选项,它将匹配最长的。即,"I|II|III|IV"将匹配“IV”表示“IV”,“III”表示匹配“III”

在Java中,相同的模式匹配“I”表示“IV”,“I”表示“III”。从左到右,交替匹配之间的Java选择;也就是说,因为“I”出现在正则表达式中的“III”之前,所以它匹配。如果我将正则表达式更改为"IV|III|II|I",则行为会得到纠正,但这显然不是一般的解决方案。

有没有办法让Java从交替组中选择最长的匹配,而不是选择“第一个”?

为清晰起见,代码示例:

public static void main(String[] args)
{
    Pattern p = Pattern.compile("six|sixty");
    Matcher m = p.matcher("The year was nineteen sixty five.");
    if (m.find())
    {
        System.out.println(m.group());
    }
    else
    {
        System.out.println("wtf?");
    }
}

这会输出"six"

2 个答案:

答案 0 :(得分:18)

不,它的行为正确。 Java使用NFA或正则表达式的风格,如Perl,.NET,JavaScript等,而 sed,grep或awk不同。一旦替代方案匹配,预计交替将退出,而不是最长匹配。

您可以通过在之后添加条件来强制它继续,直到整个令牌被消耗为止,这是无法满足的。这种情况可能取决于具体情况;最简单的选项是锚点($)或单词边界(\b)。

"\\b(I|II|III|IV)\\b"
编辑:我应该提一下,虽然grep,sed,awk和其他传统使用文本导向(或DFA)引擎,但你也可以找到一些使用NFA引擎的版本,甚至是两者的混合体。

答案 1 :(得分:3)

我认为一种可行的模式就像

IV|I{1,3}

请参阅http://download.oracle.com/javase/1.4.2/docs/api/java/util/regex/Pattern.html

中的“贪婪量词”部分

编辑:为了回应你的评论,我认为一般的问题是你在使用不正确时继续使用轮换。在你的新例子中,你试图匹配“六”或“六十”;要使用的正确模式是six(ty)?,而不是six|sixty。通常,如果您有一个交替组的两个成员,使得一个是另一个的前缀,您应该重写正则表达式以消除它。否则,你不能真的抱怨引擎做错了,因为交替的语义没有说出最长的匹配。

编辑2:你的问题的字面答案是否定的,不能强迫(我的评论是你不应该需要这种行为)。

编辑3:更多地思考这个主题,我想到一个字符串是另一个字符串前缀的交替模式是另一个原因所不可取的;也就是说,除非构建底层自动机以考虑前缀,否则速度会慢一些(鉴于Java选择模式中的第一个匹配,我猜这不是这种情况)。