Регулярні вирази: чи є оператор 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.