Java正则表达式替换运算符“|&引用;行为似乎不正常

Java正则表达式替换运算符“|&引用;行为似乎不正常,java,regex,regex-alternation,Java,Regex,Regex Alternation,试图为罗马数字编写正则表达式匹配器。在sed中(我认为它被视为regex的“标准”)如果您有多个由alternation操作符分隔的选项,它将匹配最长的选项。也就是说,“I | II | III | IV”将匹配“IV”代表“IV”和“III”代表“III” 在Java中,相同的模式匹配“I”表示“IV”,匹配“I”表示“III”。结果是Java在从左到右的交替匹配之间进行选择;也就是说,因为正则表达式中“I”出现在“III”之前,所以它匹配。如果我将正则表达式更改为“IV | III | II

试图为罗马数字编写正则表达式匹配器。在sed中(我认为它被视为regex的“标准”)如果您有多个由alternation操作符分隔的选项,它将匹配最长的选项。也就是说,
“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?");
    }
}

这将输出“六个”

我认为一个模式会起作用

IV|I{1,3}

请参阅第页的“贪婪量词”部分

编辑:作为对你评论的回应,我认为普遍的问题是,你一直在使用交替,而它不是正确的使用方式。在新示例中,您试图匹配“六”或“六十”;正确使用的模式是
six(ty)
,而不是
six | sixty
。通常,如果您有两个替换组成员,其中一个是另一个的前缀,则应该重写正则表达式以消除它。否则,您不能真的抱怨引擎做了错误的事情,因为交替的语义没有说明任何关于最长匹配的内容

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


编辑3:更多地考虑主题,我突然想到,一个字符串作为另一个字符串前缀的交替模式出于另一个原因是不可取的;也就是说,除非底层自动机的构造考虑到前缀(并且假设Java选择了模式中的第一个匹配项,我猜情况并非如此),否则它的速度会较慢。

不,它的行为是正确的。Java使用NFA或正则表达式导向的风格,如Perl、.NET、JavaScript等,与sed、grep或awk不同。一个候补者在其中一个候补者匹配时会立即退出,而不是等待最长的比赛

您可以通过在替换之后添加一个条件来强制它继续,该条件在整个令牌被使用之前无法满足。这种情况可能是什么取决于上下文;最简单的选项是锚(
$
)或单词边界(
\b


编辑:我应该提到的是,虽然grep、sed、awk和其他一些传统上使用文本导向(或DFA)引擎,但您也可以找到其中一些使用NFA引擎的版本,甚至是两者的混合版本。

这样就解决了这个特殊情况,而不是一般的行为。请看我刚才添加的代码示例。这是因为您在不需要替换的情况下一直使用替换。我将更新我的答案以更详细地解释。您所说的“标准行为”是一个特定的标准(POSIX)-据我所知,这是唯一要求交替返回最长匹配的标准。考虑到交替并不意味着最长匹配是正确的,没有任何理由期望任意匹配算法选择最长匹配。考虑到regexp“六|六十”,如果规则不是“最长匹配”,很难看到规则是什么。如果规则是“短匹配”或“第一名”,那么“六|六十”将等同于“六”,这是愚蠢的。还剩什么?@danben——我希望很明显我是在批评创造者而不是创造者。+1指出这是NFA对DFA的事情。不过,“regex导向风味”是什么意思?OP的另一个选择是安排备选方案,使它们的前缀都不在前面。对于文字模式,最简单的方法就是按大小降序排序。非常感谢。我很失望我不能强制Java默认为最长匹配,但对原因的解释和你们提供的解决方法非常满意。@Laurence:这里有一个关于正则表达式导向引擎和文本导向引擎的简要描述:……Friedl在书中对此进行了详细的阐述:@Laurence:是的,按长度递减对备选方案进行排序是一种很好的做法。如果还有其他合理的选择,我也建议不要使用交替。例如,使用
six(?:ty)
而不是
six | sixty
sixty | six
@Alan:谢谢。我知道NFA和DFA的区别,我只是从来没有听说过这些“regex-directed”和“text-directed”的术语。你知道这些术语起源于哪里吗?哪里可能有(不太好的)方法。将上述模式分为不同的模式,并自己执行裁决。
"\\b(I|II|III|IV)\\b"