Регулярное выражение: отрицательный прогноз между двумя совпадениями

Я пытаюсь построить регулярное выражение примерно так:

[match-word] ... [exclude-specific-word] ... [match-word]

Кажется, это работает с негативным прогнозом, но я сталкиваюсь с проблемой, когда у меня есть такой случай:

[match-word] ... [exclude-specific-word] ... [match-word] ... [excluded word appears again]

Я хочу, чтобы приведенное выше предложение совпадало, но отрицательный просмотр вперед между первым и вторым совпадающим словом «перетекает», поэтому второе слово никогда не совпадет.

Давайте рассмотрим практический пример.

Я не хочу сопоставлять каждое предложение, в котором есть слово «i» и слово «пирог», но не слово «ненависть» между этими двумя словами. У меня есть три предложения:

i sure like eating pie, but i love donuts <- Want to match this
i sure like eating pie, but i hate donuts <- Want to match this
i sure hate eating pie, but i like donuts <- Don't want to match this

У меня есть это регулярное выражение:

^i(?!.*hate).*pie          - have removed the word boundaries for clarity, original is: ^i\b(?!.*\bhate\b).*\bpie\b 

Что соответствует первому предложению, но не второму, потому что отрицательный просмотр вперед сканирует всю строку.

Есть ли способ ограничить негативное прогнозирование, чтобы оно было удовлетворено, если оно столкнется с «пирогом» до того, как столкнется с «ненавистью»?

Примечание: в моей реализации могут быть другие термины, следующие за этим регулярным выражением (оно построено динамически из поисковой системы грамматики), например:

^i(?!.*hate).*pie.*donuts

В настоящее время я использую JRegex, но, возможно, при необходимости могу переключиться на JDK Regex.

Обновление: я забыл кое-что упомянуть в своем первоначальном вопросе:

Вполне возможно, что «отрицательная конструкция» существует дальше в предложении, и я действительно хочу сопоставить предложение, если это возможно, даже если «отрицательная» конструкция существует дальше.

Чтобы уточнить, посмотрите на эти предложения:

i sure like eating pie, but i love donuts <- Want to match this
i sure like eating pie, but i hate donuts <- Want to match this
i sure hate eating pie, but i like donuts <- Don't want to match this
i sure like eating pie, but i like donuts and i hate making pie <- Do want to match this

Ответ Роба отлично подходит для этого дополнительного ограничения, поэтому я принимаю это.


person Alexander Malfait    schedule 23.03.2012    source источник


Ответы (3)


В каждом символе между вашим начальным и стоп-словом вы должны убедиться, что он не соответствует вашему отрицательному или стоп-слову. Вот так (где я добавил немного пробела для удобочитаемости):

^i ( (?!hate|pie) . )* pie

Вот программа python для проверки вещей.

import re

test = [ ('i sure like eating pie, but i love donuts', True),
         ('i sure like eating pie, but i hate donuts', True),
         ('i sure hate eating pie, but i like donuts', False) ]

rx = re.compile(r"^i ((?!hate|pie).)* pie", re.X)

for t,v in test:
    m = rx.match(t)
    print t, "pass" if bool(m) == v else "fail"
person rob    schedule 23.03.2012
comment
пробел в регулярном выражении не помогает читабельности, это только сбивает с толку - person Aprillion; 24.03.2012
comment
@death Пробельное пространство допустимо в регулярном выражении Python с подробным флагом. Смущает вас, помогает мне... у нас разные мнения. (Это также легко отредактировать.) - person rob; 24.03.2012
comment
так почему вы не использовали пробел в своем примере с Python? - person Aprillion; 24.03.2012
comment
спасибо, Роб, я бы никогда не понял этого сам, но это имеет смысл! - person Alexander Malfait; 24.03.2012

Это регулярное выражение должно работать для вас

^(?!i.*hate.*pie)i.*pie.*donuts

Пояснение

"^" +          // Assert position at the beginning of a line (at beginning of the string or after a line break character)
"(?!" +        // Assert that it is impossible to match the regex below starting at this position (negative lookahead)
   "i" +          // Match the character “i” literally
   "." +          // Match any single character that is not a line break character
      "*" +          // Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
   "hate" +       // Match the characters “hate” literally
   "." +          // Match any single character that is not a line break character
      "*" +          // Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
   "pie" +        // Match the characters “pie” literally
")" +
"i" +          // Match the character “i” literally
"." +          // Match any single character that is not a line break character
   "*" +          // Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
"pie" +        // Match the characters “pie” literally
"." +          // Match any single character that is not a line break character
   "*" +          // Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
"donuts"       // Match the characters “donuts” literally
person Narendra Yadala    schedule 23.03.2012

Чтобы не совпадало C между ...A...B...

Тест в python:

$ python
>>> import re
>>> re.match(r'.*A(?!.*C.*B).*B', 'C A x B C')
<_sre.SRE_Match object at 0x94ab7c8>

Итак, я получаю это регулярное выражение:

.*\bi\b(?!.*hate.*pie).*pie
person kev    schedule 23.03.2012