it-roy-ru.com

Регулярные выражения: есть оператор AND?

Очевидно, что вы можете использовать | (pipe?) Для представления OR, но есть ли способ также представить AND?

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

611
Hugoware

Используйте непотребляющее регулярное выражение.

Типичная (т.е. Perl/Java) нотация:

(?=выражение)

Это означает "match expr, но после этого продолжайте сопоставление в исходной точке совпадения".

Вы можете сделать столько, сколько захотите, и это будет "и". Пример:

(?=match this expression)(?=match this too)(?=oh, and this)

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

344
Jason Cohen

Вы должны использовать lookahead, как сказали некоторые другие респонденты, но lookahead должен учитывать другие символы между его целевым Word и текущей позицией соответствия. Например:

(?=.*Word1)(?=.*Word2)(?=.*Word3)

Код .* в первом окне позволяет ему сопоставить любое количество символов, необходимое для того, чтобы перейти к "Word1". Затем позиция совпадения сбрасывается, и второй ищущий ищет "Word2". Сброс снова, и финальная часть соответствует "Word3"; Так как это последнее Слово, которое вы проверяете, не обязательно, чтобы оно было в предвкушении, но это не повредит.

Чтобы соответствовать целому абзацу, необходимо закрепить регулярное выражение на обоих концах и добавить последний .*, чтобы использовать оставшиеся символы. Используя нотацию в стиле Perl, это будет:

/^(?=.*Word1)(?=.*Word2)(?=.*Word3).*$/m

Модификатор 'm' предназначен для многострочного режима; он позволяет ^ и $ совпадать на границах абзаца ("границы строк" ​​в регулярном выражении). В этом случае важно, чтобы вы не использовали модификатор 's', который позволяет метасимволу точки соответствовать символам новой строки и всем остальным символам.

Наконец, вы хотите убедиться, что вы соответствуете целым словам, а не только фрагментам более длинных слов, поэтому вам нужно добавить границы Word:

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m
303
Alan Moore

Посмотрите на этот пример:

У нас есть 2 регулярных выражения A и B, и мы хотим сопоставить их обоих, поэтому в псевдокоде это выглядит так:

pattern = "/A AND B/"

Это можно записать без использования оператора AND следующим образом:

pattern = "/NOT (NOT A OR NOT B)/"

в PCRE:

"/^(^A|^B)/"

regexp_match(pattern,data)
29
fanjabi

Вы можете сделать это с помощью регулярного выражения, но, вероятно, вы захотите к другому. Например, используйте несколько регулярных выражений и объедините их в предложении if.

Вы можете перечислить все возможные перестановки со стандартным регулярным выражением, например так (соответствует a, b и c в любом порядке):

(abc)|(bca)|(acb)|(bac)|(cab)|(cba)

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

Если вы используете какую-то расширенную версию регулярного выражения, такую ​​как Perl или Java, у них есть лучшие способы сделать это. Другие ответы предложили использовать положительную прогнозную операцию.

27
Juha Syrjälä

Оператор AND неявный в синтаксисе RegExp.
Вместо этого оператор OR должен указываться с помощью канала.
Следующий RegExp:

var re = /ab/;

означает букву aИ буква b.
Также работает с группами:

var re = /(co)(de)/;

это означает группу coИ группу de.
Для замены (неявного) И на OR потребуются следующие строки:

var re = /a|b/;
var re = /(co)|(de)/;
20
Emanuele Del Grande

Разве в вашем случае невозможно выполнить AND для нескольких результатов сопоставления? в псевдокоде

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...
10
user54579

Почему бы не использовать awk?
с регулярным выражением awk И, OR имеет значение так просто

awk '/Word1/ && /Word2/ && /Word3/' myfile
9
mug896

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

Например

(?=[1-9][0-9]{2})[0-9]*[05]\b

будет число больше 100 и делится на 5

8
jpalecek

Вы могли бы передать свой вывод другому регулярному выражению. Используя grep, вы можете сделать это:

grep A | grep B

6
garbagecollector

В дополнение к принятому ответу

Я предоставлю вам несколько практических примеров, которые помогут вам понять некоторые из вас. Например, скажем, у нас есть эти три строки текста:

[12/Oct/2015:00:37:29 +0200] // only this + will get selected
[12/Oct/2015:00:37:x9 +0200]
[12/Oct/2015:00:37:29 +020x]

Смотри демо здесь DEMO

Здесь мы хотим выбрать знак +, но только если он после двух чисел с пробелом и до четырех. Это единственные ограничения. Мы бы использовали это регулярное выражение для достижения этого:

'~(?<=\d{2} )\+(?=\d{4})~g'

Обратите внимание, что если вы отделите выражение, оно даст вам другие результаты.

Или, возможно, вы хотите выделить какой-то текст между тегами ... но не тегами! Тогда вы можете использовать:

'~(?<=<p>).*?(?=<\/p>)~g'

для этого текста:

<p>Hello !</p> <p>I wont select tags! Only text with in</p> 

Смотри демо здесь DEMO

5
DevWL

Порядок всегда подразумевается в структуре регулярного выражения. Чтобы выполнить то, что вы хотите, вам придется сопоставлять входную строку несколько раз с различными выражениями.

То, что вы хотите сделать, это не возможно с одним регулярным выражением.

2
pilif

Используйте AND вне регулярного выражения. В PHP оператор lookahead, похоже, не работает для меня, вместо этого я использовал это

if( preg_match("/^.{3,}$/",$pass1) && !preg_match("/\s{1}/",$pass1))
    return true;
else
    return false;

Приведенное выше регулярное выражение будет соответствовать, если длина пароля составляет 3 символа или более и в нем нет пробелов.

0
Hammad Khan