Регулярні вирази: чи є оператор AND?


707

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

Зокрема, я хотів би співставити абзаци тексту, які містять ВСІ певної фрази, але не в певному порядку.


1
Ви маєте на увазі, що ви хочете знайти фрази в тексті, де кожна така фраза є дійсною перестановкою слів у заданій фразі?
Ніцше-джу

2
Я викладаю це тут, тому що три чи чотири відповіді ігнорують це. Lookahead не відповідає однаковій довжині для кожного пункту, якщо вони не закінчуються в $. Один lookahead міг би відповідати чотирьом символам, а інший 6. Наприклад, (? = A *) (? = Aab) буде відповідати aabaaaaba
Zachary Vance

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 expr, але після цього продовжуйте відповідати в початковій точці відповідності".

Ви можете робити скільки завгодно таких, і це буде "і". Приклад:

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

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


3
perl -e "q {деякі речі та речі} = ~ / (? = деякі) (? = речі) (? = речі) /? print" так ": print" ні "" друкує "ні".
Роберт П

27
Слід зазначити, що саме цей приклад називається позитивним твердженням підказки. Він має інші можливості, ніж "і". Зауважте, що текст не споживається.
страгер

7
Використання (? =) Подібного до цього призводить до регулярного вираження, яке ніколи не може досягти успіху. Але це аналог кон'юнкції до |. ОП просто помиляється в тому, що, на його думку, вирішить його проблему.
Ніцше-джу

10
perl -e "q {деякі речі та речі} = ~ /(?=.*some)(?=.*stuff)(?=.*things)/? print" так ": print" ні ""
kriss

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 для чіткої та простої відповіді, яка демонструє одне з найкращих варіантів використання lookaheads (на відміну від таких випадків, як хак для підрахунку відсоткової відповідності пароля). :)
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)

24
Це правда з точки зору формальної логіки, але тут абсолютно ніякої допомоги. У регулярних виразах НЕ може бути важче виразити, ніж І.
Алан Мур

@marvin_dpr Це працювало для мене в CMake, тоді як інші пропозиції (?=expr)ні. Здається, це залежить від реалізації.
Мелебій

38
Чи не ^означає "початок рядка" в синтаксисі регулярних виразів?
Фея лямбда

3
У регулярному вираженні ^це заперечення лише на початку класу символів. Якщо CMake не робить щось по-справжньому химерне (до того моменту, коли називати їх мову, що відповідає шаблону, "повторним виразом", можна вважати оманливим або неправильним), я здогадуюсь про те, що це працювало для вас, було поодинокою випадковістю.
трійчатка

29

Ви можете зробити це звичайним виразом, але, ймовірно, вам захочеться чогось іншого. Наприклад, скористайтеся кількома регулярними формулами та об'єднайте їх у пункті if.

Ви можете перерахувати всі можливі перестановки зі стандартним регулярним виразом, подібним до цього (відповідає a, b і c у будь-якому порядку):

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

Однак це робить дуже тривалим і, ймовірно, неефективним повторне вираження, якщо у вас більше парних термінів.

Якщо ви використовуєте якусь розширену версію regexp, наприклад Perl чи Java, у них є кращі способи це зробити. Інші відповіді пропонують використовувати позитивну операцію пошуку.


10
Я не думаю, що ваш підхід є більш неефективним, ніж 3 диски з їх катастрофічним зворотним відстеженням. Впевнені, що писати довше, але зауважте, що ви можете легко генерувати шаблон автоматично. Зауважте, що ви можете покращити його, щоб швидше не вдалося a(bc|cb)|b(ac|ca)|c(ab|ba). І найголовніше, ви можете використовувати його з усім ароматом регексу.
Казимир та Іполіт

26

Оператор AND неявний у синтаксисі RegExp.
Оператор АБО замість цього повинен бути вказаний трубою.
Наступний RegExp:

var re = /ab/;

означає букву a І букву b.
Він також працює з групами:

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

це означає групу co І групу de.
Для заміни (неявного) AND на АБО потрібні наступні рядки:

var re = /a|b/;
var re = /(co)|(de)/;

29
На жаль, це не те, про що вимагала ОП. Це знаходить щось у цьому порядку, тоді як вони хотіли їх у будь-якому порядку. Ознайомтеся з відповіддю на сайті stackoverflow.com/users/20938/alan-moore, нижче якого є правильним.
JESii

1
@JESii дякую за вашу думку, ви маєте рацію, і я неправильнопередавав запитання від Hugoware, особливо я зосередився на його першому реченні. Правильна відповідь - це правильне використання оператора lookahead, як писав AlanMoore. Так чи інакше, я думаю, хтось може вважати моє роз'яснення корисним, як це вже було схвалено, тому я б не відкидав все. З повагою
Емануель Дель Гранде

13

Чи не можливо у вашому випадку зробити І на кількох результатах? в псевдокоді

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

3
Я знаходжусь у ситуації, коли у мене є якийсь код, який є таблицею даних правил, з єдиним рядком відповідності шаблону регулярних виразів для перевірки дійсності правила. Перехід до декількох тестів - це не те, що я можу зробити у своєму випадку, а зазвичай і у випадках інших людей!
Алан Вулф

11

Чому б не використати awk?
з awk-регулярним виразом ІЛИ, АБО, це так просто

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

9

Якщо ви використовуєте регулярні вирази Perl, ви можете використовувати позитивний lookahead:

Наприклад

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

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


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


Яка відповідь була прийнятою відповіддю? Будь ласка, додайте до неї посилання для майбутнього.
Джеймс Браун

6

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

Те , що ви хочете зробити , це НЕ можливо з одним регулярним виразом.


Технічно неможливо, але впроваджувати не варто. Я не знаю, чому хтось прихильнився ...
Роберт П

13
Можливо, тому, що це не тільки можливо, це просто, якщо припустити, що ваш аромат регулярного виразки підтримує лукахеди. І це гарна ставка; Більшість сучасних мов програмування дійсно їх підтримують.
Алан Мур

3

Використовуйте І поза регулярним виразом. У PHP оператор lookahead, здається, не працював на мене, замість цього я використав це

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

Вищенаведений регулярний вираз буде відповідати, якщо довжина пароля становить 3 символи або більше, і в ньому немає пробілів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.