Подтвердить что ты не робот

Оператор чередования регулярных выражений Java "|" поведение кажется сломанным

Попытка написать регулярное выражение для римских цифр. В sed (который, я думаю, считается "стандартным" для регулярного выражения?), Если у вас есть несколько опций, ограниченных оператором чередования, он будет соответствовать самому длинному. А именно, "I|II|III|IV" будет соответствовать "IV" для "IV" и "III" для "III"

В Java такой же шаблон соответствует "I" для "IV" и "I" для "III". Оказывается, Java выбирает между чередованием совпадений слева направо; то есть, потому что "Я" появляется перед "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"

4b9b3361

Ответ 1

Нет, он ведет себя правильно. Java использует NFA или ориентированный на регулярные выражения, например Perl,.NET, JavaScript и т.д., И в отличие от sed, grep или awk. Ожидается, что чередование прекратится, как только один из вариантов будет соответствовать, а не продержаться до самого длинного совпадения.

Вы можете заставить его продолжить, добавив условие после чередования, которое не может быть выполнено до тех пор, пока не будет уничтожен весь токен. Какое это условие может зависеть от контекста; самым простым вариантом будет якорь ($) или граница слова (\b).

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

EDIT: Я должен упомянуть, что, хотя grep, sed, awk и другие традиционно используют текстовые (или DFA) движки, вы также можете найти версии некоторых из них, которые используют двигатели NFA или даже гибриды этих двух.

Ответ 2

Я думаю, что шаблон, который будет работать, похож на

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 выбирает первое совпадение в шаблоне, я бы предположил, что это не так).