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

494

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

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

  • 1
    Вы имеете в виду, что вы хотите найти фразы в тексте, где каждая такая фраза является действительной перестановкой слов в данной фразе?
  • 2
    Я помещаю это здесь, потому что три или четыре ответа игнорируют это. Lookahead не соответствует одинаковой длине для каждого предложения, если только они не заканчиваются на $. Один взгляд может соответствовать четырем символам, а другой 6. Например, (? = A *) (? = Aab) будет соответствовать aabaaaaba
Показать ещё 2 комментария
Теги:
lookahead

13 ответов

277

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

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

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

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

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

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

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

  • 0
    Вы просто размещаете их все подряд, без разделителей? то есть (? = яблоко) (? = апельсин) (? = груша)
  • 0
    Разделители (или любые символы) указывают на то, что они должны существовать до следующей непотребляющей группы.
Показать ещё 8 комментариев
263

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

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

.* в первом lookahead позволяет ему сопоставлять любое количество символов, которое ему нужно, прежде чем оно попадет в "word1". Тогда позиция соответствия reset, а второй lookahead ищет слово "word2". reset снова, а заключительная часть соответствует "word3"; так как это последнее слово, которое вы проверяете, не обязательно, чтобы оно было в поиске, но это не повредило.

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

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

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

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

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m
  • 6
    Совершенно верно - об этом тоже есть учебник! ocpsoft.org/tutorials/regular-expressions/and-in-regex
  • 9
    Большое спасибо. * Это имеет значение
Показать ещё 5 комментариев
23

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

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

pattern = "/A AND B/"

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

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

в PCRE:

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

regexp_match(pattern,data)
  • 21
    Это верно с точки зрения формальной логики, но здесь это абсолютно не поможет. В регулярных выражениях NOT может быть даже сложнее выразить, чем AND.
  • 0
    Умная точка. любое И может быть составлено с использованием НЕ и ИЛИ
Показать ещё 6 комментариев
23

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

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

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

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

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

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

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

var re = /ab/;

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

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

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

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

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

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile
8

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

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

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

Например

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

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

6

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

grep A | grep B

1

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

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

[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

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

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

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

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

-2

Самый простой способ сделать это - это просто использовать два регулярных выражения, соединенных с логическим оператором and, предоставленным вашим языком. В синтаксисе Perl это будет выглядеть так:

if ( /phrase 1/ and /phrase 2/ ) {
    // it a match
}

Если вы хотите сделать одно и то же в одном регулярном выражении, вы всегда можете найти "фразу 1, за которой следует фраза 2" ИЛИ "фраза 2, за которой следует фраза 1"

if ( /phrase 1.*phrase 2|phrase 2.*phrase 1/ ) {
    // it a match
}

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

-3

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню