Регулярные выражения: есть оператор AND? - PullRequest
609 голосов
/ 22 января 2009

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

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

Ответы [ 12 ]

345 голосов
/ 22 января 2009

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

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

(?= выраж )

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

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

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

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

300 голосов
/ 22 января 2009

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

(?=.*word1)(?=.*word2)(?=.*word3)

.* в первом взгляде позволяет ему сопоставить сколько угодно символов, прежде чем он доберется до «word1». Затем позиция совпадения сбрасывается, и второй ищущий ищет «word2». Сброс снова, и последняя часть соответствует «word3»; так как это последнее слово, которое вы проверяете, не обязательно, чтобы оно было в поле зрения, но это не повредит.

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

/^(?=.*word1)(?=.*word2)(?=.*word3).*$/m

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

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

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m
29 голосов
/ 20 апреля 2011

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

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

pattern = "/A AND B/"

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

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

в PCRE:

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

regexp_match(pattern,data)
27 голосов
/ 22 января 2009

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

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

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

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

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

20 голосов
/ 30 июня 2014

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

var re = /ab/;

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

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

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

var re = /a|b/;
var re = /(co)|(de)/;
10 голосов
/ 22 января 2009

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

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...
9 голосов
/ 27 декабря 2013

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

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile
8 голосов
/ 22 января 2009

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

Например

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

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

6 голосов
/ 21 ноября 2014

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

grep A | grep B

5 голосов
/ 13 ноября 2015

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

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

[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

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