Отрицательная жадность Rexx Lookahead (почему. *? Слишком жадный) - PullRequest
19 голосов
/ 25 мая 2011

У меня проблемы с пониманием мелких деталей регулярных выражений с отрицательным прогнозом. Прочитав Regex lookahead, lookbehind и atomic groups , я подумал, что у меня было хорошее резюме негативных взглядов, когда я нашел это описание:

(?!REGEX_1)REGEX_2

Совпадение, только если REGEX_1 не совпадает; после проверки REGEX_1 поиск REGEX_2 начинается с той же позиции.

Надеясь, что я понял алгоритм, я приготовил тестовое оскорбление из двух предложений; Я хотел найти предложение без определенного слова. В частности ...

Оскорбление: 'Йомама безобразна. И она пахнет как мокрая собака. '

Требования

  • Тест 1: вернуть предложение без «уродливого».
  • Тест 2: вернуть предложение без «взглядов».
  • Тест 3: вернуть предложение без «запахов».

Я назначил тестовые слова для $arg и использовал (?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.) для реализации теста.

  • (?![A-Z].*?$arg.*?\.) - отрицательный взгляд на отклонение предложения с тестовым словом
  • ([A-Z].*?\.) соответствует как минимум одно предложение.

Важным моментом, по-видимому, является понимание того, где механизм регулярных выражений начинает сопоставляться после обработки отрицательного результата.

Ожидаемые результаты :

  • Тест 1 ($ arg = "ugly"): "И она пахнет как мокрая собака".
  • Тест 2 ($ arg = "look"): "Йомама безобразна".
  • Тест 3 ($ arg = "запах"): "Йомама безобразна".

Фактические результаты :

  • Тест 1 ($ arg = "ugly"): "И она пахнет как мокрая собака". (Success)
  • Тест 2 ($ arg = "look"): "Йомама безобразна". (Success)
  • Тест 3 ($ arg = "smells"): Не удалось, не найдено совпадений

Сначала я думал, что Тест 3 провалился, потому что ([A-Z].*?\.) был слишком жадным и соответствовал обоим предложениям; однако (?:(?![A-Z].*?$arg.*?\.))([A-Z][^\.]*?\.) тоже не сработало. Затем я задался вопросом, была ли проблема с реализацией python негативного просмотра, но perl дал мне точно такой же результат.

Наконец, я нашел решение, мне пришлось отклонить точки в моей .*? части выражений, используя [^\.]*?; так что это регулярное выражение работает: (?:(?![A-Z][^\.]*?$arg[^\.]*?\.))([A-Z][^\.]*?\.)

Вопрос

Однако у меня есть другая проблема; "Йомама безобразна". не имеет "запахов" в нем. Итак, если .*? считается не жадным совпадением, почему я не могу завершить Тест 3 с (?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)?

EDIT

В свете превосходного предложения @ bvr использовать -Mre=debug, я рассмотрю это еще немного после работы. Похоже, что описание Сета на данный момент точное. До сих пор я узнал, что отрицательные прогнозные выражения будут совпадать всякий раз, когда это возможно, даже если я добавлю не жадные операторы .*? в NLA.


Реализация Python

import re

def test_re(arg, INSULTSTR):
    mm = re.search(r'''
        (?:                  # No grouping
        (?![A-Z].*?%s.*?\.)) # Negative zero-width
                             #     assertion: arg, followed by a period
        ([A-Z].*?\.)         # Match a capital letter followed by a period
        ''' % arg, INSULTSTR, re.VERBOSE)
    if mm is not None:
        print "neg-lookahead(%s) MATCHED: '%s'" % (arg, mm.group(1))
    else:
        print "Unable to match: neg-lookahead(%s) in '%s'" % (arg, INSULTSTR)


INSULT = 'Yomama is ugly.  And, she smells like a wet dog.'
test_re('ugly', INSULT)
test_re('looks', INSULT)
test_re('smells', INSULT)

Реализация Perl

#!/usr/bin/perl

sub test_re {
    $arg    = $_[0];
    $INSULTSTR = $_[1];
    $INSULTSTR =~ /(?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)/;
    if ($1) {
        print "neg-lookahead($arg) MATCHED: '$1'\n";
    } else {
        print "Unable to match: neg-lookahead($arg) in '$INSULTSTR'\n";
    }
}

$INSULT = 'Yomama is ugly.  And, she smells like a wet dog.';
test_re('ugly', $INSULT);
test_re('looks', $INSULT);
test_re('smells', $INSULT);

выход

neg-lookahead(ugly) MATCHED: 'And, she smells like a wet dog.'
neg-lookahead(looks) MATCHED: 'Yomama is ugly.'
Unable to match: neg-lookahead(smells) in 'Yomama is ugly.  And, she smells like a wet dog.'

Ответы [ 3 ]

3 голосов
/ 25 мая 2011

Если вам интересно, что Perl делает с регулярным выражением, вы можете запустить с помощью отладчика регулярного выражения:

perl -Dr -e '"A two. A one." =~ /(?![A-Z][^\.]*(?:two)[^\.]*\.)([A-Z][^\.]+\.)/; print ">$1<\n"'

, который будет генерировать значительный вывод для размышления.Вам понадобится Perl, созданный с -DDEBUGGING.

3 голосов
/ 25 мая 2011
#!/usr/bin/perl

sub test_re {
    $arg    = $_[0];
    $INSULTSTR = $_[1];
    $INSULTSTR =~ /(?:^|\.\s*)(?:(?![^.]*?$arg[^.]*\.))([^.]*\.)/;
    if ($1) {
        print "neg-lookahead($arg) MATCHED: '$1'\n";
    } else {
        print "Unable to match: neg-lookahead($arg) in '$INSULTSTR'\n";
    }
}

$INSULT = 'Yomama is ugly.  And, she smells like an wet dog.';
test_re('Yomama', $INSULT);
test_re('ugly', $INSULT);
test_re('looks', $INSULT);
test_re('And', $INSULT);
test_re('And,', $INSULT);
test_re('smells', $INSULT);
test_re('dog', $INSULT);

Результаты:

neg-lookahead(Yomama) MATCHED: 'And, she smells like an wet dog.'
neg-lookahead(ugly) MATCHED: 'And, she smells like an wet dog.'
neg-lookahead(looks) MATCHED: 'Yomama is ugly.'
neg-lookahead(And) MATCHED: 'Yomama is ugly.'
neg-lookahead(And,) MATCHED: 'Yomama is ugly.'
neg-lookahead(smells) MATCHED: 'Yomama is ugly.'
neg-lookahead(dog) MATCHED: 'Yomama is ugly.'
2 голосов
/ 25 мая 2011

Ваша проблема в том, что движок регулярных выражений будет стараться изо всех сил соответствовать (?![A-Z].*?$arg.*?\.), поэтому в случае "запахов" он в конечном итоге будет соответствовать всей строке.(Точка в середине затем включается в одну из конструкций .*?.) Вы должны ограничить случай с отрицательным прогнозом таким образом, чтобы он совпадал только с тем, что может сделать другой случай:

Вместо:

(?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)

Использование:

(?:(?![A-Z][^.]*$arg[^.]*\.))([A-Z].*?\.)

Теперь отрицательный прогноз не может сопоставить больше строки, чем другая часть, поскольку он должен остановиться в первый период.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...