[Из песочницы] Поиск регулярных выражений с помощью регулярных выражений
Приветствую уважаемые.
«Ехали регулярные выражения, через регулярные выражения, видят регулярные выражения, в регулярных выражениях, регулярные выражения — регулярные выражения, регулярные выражения, регулярные выражения…»
Нет. Это не бред сумасшедшего. Именно так я хотел назвать мой небольшой обзор на тему поиска регулярных выражений с помощью регулярных выражений. Что по сути тоже не меньший бред. Даже не знаю может ли вам такое в жизни пригодиться. Лучше конечно избегать таких ситуаций когда надо искать непонятно что, непонятно где. Ведь что такое регулярное выражение? Да почти всё что угодно!
Вам может показаться странным, но:
.это, например, вполне себе регулярное выражение:.
(Или это тоже может быть (можете даже проверить))
~это~
Но давайте без паники, попробуем приступить, может что и выйдет приличное.
Регулярное выражение — это нечто в ограничителях и возможно с модификаторами в конце. К примеру, что-нибудь такое:
/регулярное выражение/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)(? #Модификаторы шаблонов
#PHP [mixXsuUAJ] JavaScript [gmi] python [gmixsu]
((?(6)(?:[mixXsuUAJ]*)|(?(?=.*?[mixXsuUAJ]+)[mixXsuUAJ]+)))/xs
Энтузиазм у меня ещё не угас, но навалилась работа. Если у кого есть желание — можно помучиться.
Текущие цели и задачи:
1) Еще не решена проблема с ограничителями-скобками. К сожалению для нас скобки можно не экранизировать внутри:
((регулярное)(выражение))isu
2) Нужно добавить игнорирование ограничителей между # и переводом строки
3) Закрывающие парные ограничители в комментариях
4) Красиво решить проблему с экранизированными \ перед последним ограничителем.
Ссылка на последний вариант, для желающих помочь довести дело до конца
Спасибо за внимательное внимание компьютерные маньяки.