12

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

在 Java 中,相同的模式匹配“IV”的“I”和“III”的“I”。原来 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"

4

2 回答 2

20

不,它的行为正确。Java 使用 NFA 或正则表达式导向风格,如 Perl、.NET、JavaScript 等,sed、grep 或 awk 不同。一旦其中一个备选方案匹配,预计将立即退出,而不是坚持最长的匹配。

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

"\\b(I|II|III|IV)\\b"

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

于 2010-12-23T02:35:34.953 回答
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 选择了模式中的第一个匹配项,我猜情况并非如此)。

于 2010-12-23T02:08:04.530 回答