Пошук регулярних виразів з допомогою регулярних виразів

Вітаю шановні.

«Їхали регулярні вирази, через регулярні вирази, бачать регулярні вирази, в регулярних виразах, регулярні вирази — регулярні вирази, регулярні вирази, регулярні вирази...»

Немає. Це не марення божевільного. Саме так я хотів назвати мій невеликий огляд на тему пошуку регулярних виразів за допомогою регулярних виразів. Що, по суті, теж не менший маячня. Навіть не знаю може вам таке в житті нагоді. Краще звичайно уникати таких ситуацій, коли треба шукати незрозуміло що, незрозуміло де. Адже що таке формальний вираз? Так майже все що завгодно!

Вам може здатися дивним, але:

.це, наприклад, цілком собі регулярний вираз:.
(Чи це теж може бути (можете навіть перевірити))
~це~
<script src="І це — регулярка, цілком робоча і може бути навіть кому-небудь дуже необхідна.js">


Але давайте без паніки, спробуємо приступити, може що і вийде пристойна.

Регулярний вираз – це щось в обмежувачах і можливо з модифікаторами в кінці. Наприклад, що-небудь таке:

/регулярний вираз/isux

Обмежувачем в регулярному виразі PCRE може бути не-цифра, літера, не-пробільний символ, не зворотний слеш. [^\s\w\\] До того ж цей символ одночасно повинен бути з ASCII: [[:ascii:]], інакше можна зловити всілякі цікавості ︷типу︷ ¼таких¼ ™як™… ці...
Не треба мене питати, кому таке може в голову прийти.

Існують також парні обмежувачі: ()[]{}<>. Тобто першим обмежувачем не може бути парний закриваючий обмежувач: [^\s\w\\\)\]\}\>]

Разом маємо умову пошуку для першого обмежувача:

(?=[[:ascii:]]) [^\s\w\\\)\]\}\>]

На жаль, ми не зможе перевірити, який саме символ потрапив до нас в якості першого обмежувача, але ми можемо ловити в дужки окремо парні <,(,[,{ символи:

Регулярний вираз для пошуку одного регулярного виразу/(

(\<) | (\() | (\[) | (\{) |
((?=[[:ascii:]])[^\s\w\\\)\]\}\>])

)


#А потім поставити до нього відповідний закриваючий обмежувач:
(.*) (?(2)\>) (?(3)\)) (?(4)\]) (?(5)\}) (?(6)\6)

#Ну і можна залакувати все це справа модифікаторами:
([mixXsuUAJ]*)/xs

Цей регулярний вираз знайде і розпатрає на: ([1] => обмежувач, [7] => шаблон, [8] =>модифікатори) тільки одне регулярний вираз. Т. к. використовується жадібний квантіфікатор .*, який їсть все до кінця, а потім тільки бэктрекает до найближчого збігання. При великому бажанні воно може розпотрошити саме себе.

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

По-перше, потрібно використовувати ледачий квантіфікатор (.*?)

По-друге, потрібно шукати збіг з неекранованим обмежувачем, який, у свою чергу, може виявитися волею доль закомментированным. А як вам варіант обмежувача з экранизированным зворотним слешем перед ним? / \\ \/ \\/is

Ласкаво просимо в пекло:

((?#ignore comments like this in the regular expression)
(?(6)
(?(?=
(?:(?!\6).|(?<=\\)\6)*[^\\][\(][\?][\#])
(?:(?!\6).|(?<=\\)\6)*[^\\][\(][\?][\#]
[^\)]*
(?-1)
))
.*?)


Трохи поясню даний код:

1) Ми не можемо шукати [^\6], т. к. в символьному класі наш покажчик втрачає свою чарівну силу. Але завдяки випереджальної негативної перевірки ми можемо перевірити будь-який символ: [^\6]* => ((?!\6).)*
2) (?(?=рядок)рядок) – може здатися безглуздим, але це необхідно у випадках коли потрібно щось додати.
3) (?-1) – при збігу знову перевіряти на збіг. В даному випадку ми шукаємо, наприклад, збіг (?# / у разі знаходження захоплюємо до закриваючої дужки.

Разом, на даний момент, ми маємо наступне:

Регулярний вираз для пошуку регулярних виразів/#Обмежувач 1
((\<)|(\()|(\[)|(\{)|
((?=[[:ascii:]])[^\s\w\\\)\]\}\>]))
#Шаблон
((?#ignore comments like this in the regular expression)
(?(6)
(?(?=(?:(?!\6).|(?<=\\)\6)*[^\\][\(][\?][\#])
(?:(?!\6).|(?<=\\)\6)*[^\\][\(][\?][\#]
[^\)]*(?-1)))
.*?)
#Обмежувач 2
#екранізовані зворотні скісні риски +
#неэкранизированный обмежувач
(?(2)(?<!(?<!(?<!(?<!(?<!(?<!(?<!(?<!\\)\\)\\)\\)\\)\\)\\)\\)\>)
(?(3)(?<!(?<!(?<!(?<!(?<!(?<!(?<!(?<!\\)\\)\\)\\)\\)\\)\\)\\)\))
(?(4)(?<!(?<!(?<!(?<!(?<!(?<!(?<!(?<!\\)\\)\\)\\)\\)\\)\\)\\)\])
(?(5)(?<!(?<!(?<!(?<!(?<!(?<!(?<!(?<!\\)\\)\\)\\)\\)\\)\\)\\)\})
(?(6)(?<!(?<!(?<!(?<!(?<!(?<!(?<!(?<!\\)\\)\\)\\)\\)\\)\\)\\)\6)
#Модифікатори шаблонів
#PHP [mixXsuUAJ] JavaScript [gmi] python [gmixsu]
((?(6)(?:[mixXsuUAJ]*)|(?(?=.*?[mixXsuUAJ]+)[mixXsuUAJ]+)))/xs


Ентузіазм у мене ще не згас, але навалилася робота. Якщо у кого є бажання — можна помучитися.

Поточні цілі і завдання:

1) Ще не вирішена проблема з обмежувачами-дужками. На жаль для нас дужки не екранізувати всередині:
((регулярне)(вираз))isu
2) Потрібно додати ігнорування обмежувачів між # і переведенням рядка
3) Закривають парні обмежувачі в коментарях
4) Красиво вирішити проблему з екранізованими \ перед останнім обмежувачем.

Посилання на останній варіант, для бажаючих допомогти довести справу до кінця

Дякую за уважне увагу комп'ютерні маніяки.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.