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

Я пытаюсь сопоставить некоторый текст, если рядом с ним нет другого блока текста. Например, я хотел бы сопоставить "bar", если ему не предшествует "foo". Я могу сопоставить "bar", если "foo" не непосредственно предшествует ему, используя отрицательный просмотр в этом регулярном выражении:

/(?<!foo)bar/

но мне также нравится не соответствовать "foo 12345 bar". Я старался:

/(?<!foo.{1,10})bar/

но использование подстановочного знака + диапазона кажется недопустимым регулярным выражением в Ruby. Я неправильно думаю о проблеме?


person Kevin Eder    schedule 30.11.2012    source источник


Ответы (2)


Вы думаете об этом правильно. Но, к сожалению, lookbehinds обычно имеют фиксированную длину. Единственным существенным исключением из этого является механизм регулярных выражений .NET, который позволяет квантификаторы повторения внутри просмотра назад. Но так как вам нужен только негативный взгляд назад, а не взгляд вперед. Для вас есть хак. Переверните строку, затем попробуйте сопоставить:

/rab(?!.{0,10}oof)/

Затем измените результат совпадения или вычтите совпадающую позицию из длины строки, если это то, что вам нужно.

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

Обратите внимание, что если ваш шаблон требует как просмотра назад, так и просмотра вперед переменной длины, вам будет труднее решить эту проблему. Кроме того, в вашем случае можно было бы разобрать ваш внешний вид на несколько переменных длины (поскольку вы не используете ни +, ни *):

/(?<!foo)(?<!foo.)(?<!foo.{2})(?<!foo.{3})(?<!foo.{4})(?<!foo.{5})(?<!foo.{6})(?<!foo.{7})(?<!foo.{8})(?<!foo.{9})(?<!foo.{10})bar/

Но не все так хорошо, не так ли?

person Martin Ender    schedule 30.11.2012
comment
Перевернуть струну было интересной идеей. Спасибо! - person Kevin Eder; 03.12.2012

Как уже упоминал m.buettner, просмотр назад в регулярном выражении Ruby должен иметь фиксированную длину, и это описано в документе. Таким образом, вы не можете поместить квантификатор в ретроспективный анализ.

Вам не нужно проверять все за один шаг. Попробуйте выполнить несколько шагов совпадения регулярных выражений, чтобы получить то, что вы хотите. Предполагая, что существование foo перед одним экземпляром bar нарушает условие независимо от того, существует ли другой bar, тогда

string.match(/bar/) and !string.match(/foo.*bar/)

даст вам то, что вы хотите для примера.

Если вы хотите, чтобы совпадение было успешным с bar foo bar, вы можете сделать это

string.scan(/foo|bar/).first == "bar"
person sawa    schedule 30.11.2012
comment
Это проблематично, если идея состоит в том, чтобы фактически получить совпадение. Скажем, у вас есть bar foo bar. Регулярное выражение, которое пробовал OP, извлекло бы первое bar. Ваше решение будет утверждать, что совпадений нет. (Помимо того, что вы пропустили эвристику до 10 символов) - person Martin Ender; 01.12.2012
comment
@m.buettner m.buettner У нас с вами разные интерпретации вопроса. - person sawa; 01.12.2012
comment
Конечно. Вот почему я не говорю, что ваше решение неверно. Но я считаю важным, чтобы такие предположения и различия были изложены. Потому что они могут быть не очевидны для ОП или кого-либо еще, кто найдет этот вопрос в будущем. - person Martin Ender; 01.12.2012
comment
Спасибо за разночтения. Я принимаю ответ @m.buettner, так как это было то, что мне было нужно - person Kevin Eder; 03.12.2012