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

708

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

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

Hugoware
источник
1
Вы имеете в виду, что вы хотите найти фразы в тексте, где каждая такая фраза является действительной перестановкой слов в данной фразе?
Ницше Джоу
2
Я помещаю это здесь, потому что три или четыре ответа игнорируют это. Lookahead не соответствует одинаковой длине для каждого предложения, если только они не заканчиваются на $. Один взгляд может соответствовать четырем символам, а другой 6. Например, (? = A *) (? = Aab) будет соответствовать aabaaaaba
Захари Вэнс
2
попробуйте использовать только символ «пробел» для оператора «И».
1 I'd like to match paragraphs of text. 2. Содержащие испорченный текст. Номер 1 открыт для интерпретации. Номер 2 можно сделать несколькими способами. Способ 1:, (?:(?:(?(1)(?!))\b(phrase1)\b.*?|(?(2)(?!))\b(phrase2)\b.*?)){2}Способ 2: (?=.*\bphrase1\b)(?=.*\bphrase2\b)где в этом случае соответствие абзаца в этом случае не определено, пока определение абзаца не будет формализовано.

Ответы:

385

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

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

(?=выраж)

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

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

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

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

Джейсон Коэн
источник
3
perl -e "q {некоторые вещи и вещи} = ~ / (? = некоторые) (? = вещи) (? = вещи) /? print 'yes': print 'no' 'печатает' no '.
Роберт П
27
Следует отметить, что этот конкретный пример называется положительным прогнозным утверждением. Он имеет иное применение, чем «и». Обратите внимание, что текст не используется.
Страгер
7
Использование (? =) Как это приводит к регулярному выражению, которое никогда не может быть успешным. Но это аналог конъюнкции к |. ОП просто ошибается в том, что, по его мнению, решит его проблему.
Ницше Джоу
10
perl -e "q {некоторые вещи и вещи} = ~ /(?=.*some)(?=.*stuff)(?=.*things)/? print 'yes': напечатайте 'no'"
Крис
3
Можете ли вы добавить несколько простых примеров в коде perl в вашем ответе?
Питикос
343

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

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

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

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

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

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

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

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m
Алан Мур
источник
8
Совершенно верно - об этом тоже есть учебник! ocpsoft.org/tutorials/regular-expressions/and-in-regex
Линкольн
9
Большое спасибо. * В этом разница
Геннадий Рябкин
1
+1 для ясного и краткого ответа, демонстрирующего одно из лучших применений для поиска (в отличие от использования, такого как взлом для подсчета совпадения пароля в процентах). :)
zx81
1
@Liam :. MySQL использует разновидность POSIX ERE, поэтому нет. Он эффективно жертвует функциями в пользу производительности, что мне кажется разумным. Там больше информации здесь .
Алан Мур
3
замените .*на [\s\S]*в javascript, если у вас есть новые строки, так как .в движке javascript механизм регулярных выражений не соответствует новым строкам и не может быть
Уэсли Смит
41

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

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

pattern = "/A AND B/"

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

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

в PCRE:

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

regexp_match(pattern,data)
fanjabi
источник
24
Это верно с точки зрения формальной логики, но здесь это абсолютно не поможет. В регулярных выражениях NOT может быть даже сложнее выразить, чем AND.
Алан Мур
@marvin_dpr У меня это работало в CMake, а другое предложение - (?=expr)нет. Кажется, это зависит от реализации.
Мелебиус
38
Не ^означает «начало строки» в синтаксисе регулярных выражений?
Лямбда Фея
3
В регулярных выражениях вообще ^отрицание только в начале класса символов. Если только CMake не делает что-то действительно прикольное (до такой степени, что называть их язык сопоставления с образцом "регулярным выражением" можно считать вводящим в заблуждение или неправильным), я предполагаю, что тот факт, что это сработало для вас, был изолированной случайностью.
tripleee
29

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

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

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

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

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

Юха Сирьяля
источник
10
Я не думаю, что ваш подход более неэффективен, чем 3 вида назад с их катастрофическим отступлением. Конечно, писать дольше, но учтите, что вы можете легко сгенерировать шаблон автоматически. Обратите внимание, что вы можете улучшить его, чтобы быстрее потерпеть неудачу a(bc|cb)|b(ac|ca)|c(ab|ba). И самое главное, вы можете использовать его с любым вкусом регулярных выражений.
Казимир и Ипполит
27

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

var re = /ab/;

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

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

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

var re = /a|b/;
var re = /(co)|(de)/;
Эмануэле Дель Гранде
источник
29
К сожалению, это не то, о чем просил ОП. Это находит что-нибудь в этом порядке, тогда как они хотели их в любом порядке. Проверьте ответ по stackoverflow.com/users/20938/alan-moore ниже, который является правильным.
ИЕСИ
1
@JESii, спасибо за вашу точку зрения, вы правы, и я неправильно понял вопрос из Hugoware, я сосредоточился на его первом предложении. Правильный ответ - правильное использование оператора lookahead, как писал AlanMoore. В любом случае, я думаю, что кто-то может найти мои разъяснения полезными, так как за них уже проголосовали, так что я бы не стал все выбрасывать. С уважением.
Эмануэле Дель Гранде
13

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

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...
user54579
источник
3
Я нахожусь в ситуации, когда у меня есть некоторый код, который представляет собой таблицу данных правил, с единственной строкой соответствия шаблону регулярного выражения для проверки действительности правила. Переход к нескольким тестам - это не то, что я могу сделать в моем случае, и обычно в случаях других людей!
Алан Вулф
11

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

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile
mug896
источник
9

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

Например

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

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

jpalecek
источник
8

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

grep A | grep B

уборщик мусора
источник
8

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

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

[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

DevWL
источник
Какой ответ был принят? Пожалуйста, добавьте ссылку на него для будущего меня.
Джеймс Браун
6

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

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

pilif
источник
Это технически невозможно, но не стоит реализовывать. Я не знаю, почему кто-то проголосовал, хотя ...
Роберт Р
13
Вероятно потому, что это не только возможно, но и просто, если предположить, что ваш regex-артикль поддерживает прогнозирование. И это хорошая ставка; большинство современных языков программирования поддерживают их.
Алан Мур
3

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

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

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

Хаммад хан
источник