Номер телефона

Последнее обновление:

Регулярное выражение для поиска регулярных выражений

Приводимое в этой статье регулярное выражение НЕ ЯВЛЯЕТСЯ УНИВЕРСАЛЬНЫМ.

Регулярные выражения - это весьма мощная технология для поиска/замены подстрок в текстах, в том числе, в текстах программных кодов. Рассмотрим простые регулярные выражения:

/^(.*?)$/ - в языке javascript (JS),

'/^(.*?)$/' - в языке РНР

Например, у нас есть такой текст, представляющий собой программный код на языке JS:

var x = 'Текст1, текст2, текст3,  текст4 и еще текст5.';
var reg = /\d+,/;
    var exist = x.match(reg);
// Переменная exist будет равна 1,
    alert(exist);

И мы хотим (программным путем) узнать, содержится ли в этом тексте регулярное выражение в стиле JS. И если да, то - вывести его, к примеру, на экран или записать в отдельный файл. В данном случае мы, по сути, хотим, чтобы нашлось следующее регулярное выражение: /\d+,/.

Иногда бывает необходимо найти сами эти (или иные) регулярные выражения. Как это сделать? Есть два пути.

Первый - простой, но не универсальный. Это - запустить соответствующий программный код на том или ином языке. А потом при помощи соответствующих функций того или иного языка программирования найти регулярные выражения. Однако, это, хотя и очень простая, но нежелательная практика. Так как не всегда бывает возможно запускать программный код. Ведь он может быть взят из ненадежного источника или же просто нецелесообразно его выполнение в конкретный момент.

Второй способ - посложнее, но универсальный. Здесь придется иметь дело с программным кодом просто как с "сырым" текстом, НЕ запуская его. И, с учетом синтаксиса регулярных выражений в том или ином языке программирования, сделать разбор (т.е. распарсить) этот текст, выделив из него искомые регулярные выражения.

Пусть не смущает тавтология "регулярное выражение для поиска регулярных выражений".

Чтобы не создавать "интриг", приведем такое выражение сразу (для языка PHP, поиск может, впоследствии, производиться средствами РНР):

'~[\r\n]*(?<=[=,{\[(]|^)(?:\s*?)(/[^*][^/]+?/[gimuys]*)(?=$|[\s.,);}\]])~'

Примечания:

1. Чтобы это регулярное выражение работало корректно, из текстовой строки (представляющей собой программный код), в которой будет проводиться поиск регулярных выражений, ДОЛЖНЫ БЫТЬ удалены все последовательности символов \/ (или - заменены на какие-либо другие символы). Дело в том, что это - экранированный символ слеша ( / ). Если он будет присутствовать в текстовой строке, возможны некорректные ситуации. Дело в том, что вполне может существовать такое регулярное выражение (на примере JS):

/(.*?)\//

Как видим, в конце этого выражения присутствуют ДВА слеша подряд. Это бывает необходимо, когда требуется, чтобы слеш (являющийся также и символом-ограничителем регулярного выражения) присутствовал, как самостоятельный символ. Чтобы интерпретатор (или компилятор) языка программирования не "перепутал" такой слеш со слешем-ограничителем, необходимо делать экранирование. И если в РНР в качестве ограничителей могут использоваться и иные символы (например, кавычки, символы |, ~, !), то в JS возможностей для выбора ограничителей нет: там используется только слеш.

2. Даже при выполнении п.1 приведенное выше регулярное выражение НЕ ЯВЛЯЕТСЯ универсальным для поиска регулярных выражений в текстовых строках. В самом деле, возьмем такой пример:

/[/.12]/

Или даже нечто вроде:

/[[[/.12]/ - это уже некорректно, но, скорее всего, будет работоспособным в javascript; в ряде других языков может не работать.

Строго корректно будет так:

/[\[/.12]/

Очевидно, что приведенный выше шаблон регулярного выражения выдаст совпадение [ (или [[[, соответственно).

3. Также могут быть варианты, когда регулярное выражение находится внутри многострочного комментария, что-то вроде:

/* ...символы...  /.[123]*/ ...еще /* символы... */

Обратите внимание на */. Это может быть окончанием как регулярного выражения, так и комментария. Поэтому для того, чтобы быть точно уверенным, что найдено именно регулярное выражение, придется реализовывать какую-то дополнительную логику.

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

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

Приведенное регулярное выражение является достаточно сложным, особенно, для новичков. Поэтому сделаем подробное объяснение, что там и как.

Объяснение регулярного выражения для поиска регулярных выражений в тексте

1. Вначале сформулируем синтаксические правила, на основе которых будем производить поиск регулярных выражений (РВ) по тексту (текстовой строке):

  1. До РВ могут находиться ПРОБЕЛЫ, а перед ними должен быть ОДИН из разделителей , ( { [ =  или должно быть начало строки (т.е. символ \n),
  2. После РВ могут находиться ПРОБЕЛЫ, а перед ними должен быть ОДИН из разделителей , ; ) } ] или должен быть конец строки (т.е. символ \n),
  3. Внутри РВ не должно быть переносов строк,
  4. РВ (в языке JS) должно начинаться с символа /,
  5. РВ (в языке JS) должно заканчиваться символом /.

Вероятно, этими правилами описывается синтаксис регулярных выражений в очень многих языках программирования (за исключением, быть может, п.4-5, которые применимы, например, к JS). Нарушение этих правил будет свидетельствовать о наличии синтаксических ошибок в текстовой строке. Т.е. ее уже нельзя будет считать корректным кодом JS.

Посмотрим, насколько приведенное выше РВ соответствует этим правилам.

2.1. Начало РВ. Символы [\r\n]*. Это - указание на то, что могут присутствовать символы концов строк. Напомним, что в операционной системе Windows в качестве конца строки используется \r\n, а в Linux - \n (cовременная Windows понимает такое окончание строк тоже).

Таким образом, данная часть РВ указывает, что в его начале могут присутствовать символы концов (переносов) строк (любое количество) или же таковых символов может не присутствовать вообще (на это указывает идентификатор *). Квадратные скобки содержат перечень символов (это только \r и \n), на которые распространяется действие текущего идентификатора *. Их положение, количество в начале РВ может быть произвольным, например \r\n\r\r\r или \n\r\n\n и т.п.

2.2. Группа, обозначенная круглыми скобками (?<=[=,{\[(]|^). Эта группа начинается со сложной скобки: (?<=.

Именно так. Это - ОДНА скобка, состоящая из четырех символов. Обратите внимание, что левая квадратная скобка заэкранирована обратным слешем, как обычно.

Эта скобка обозначает позитивный просмотр назад. Или - ретроспективная проверка.

Вообще, стандартно (по умолчанию) просмотр РВ идет слева направо. Но, в некоторых случаях требуется задать условие: символы, находящиеся в текстовой строке, могут быть захвачены РВ (т.е. могут соответствовать РВ) только тогда, когда слева от них присутствуют определенные (другие) символы. Иными словами, то, что идет после этой группы (в круглых скобках), может соответствовать РВ тогда, когда слева от него будут символы, присутствующие в этих скобках.

В них мы видим конструкцию, состоящую из разделителя | (или). В данном случае, он обозначает: или [=,{\[(], или ^.

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

Что же касается символа ^, то в данном контексте он обозначает начало текстовой строки, в которой будет производиться поиск. В самом деле, ведь в текстовой строке искомое РВ может находиться в самом начале, до него может не быть вообще никаких других символов. При этом, что важно, содержимое этой скобки НЕ ВКЛЮЧАЕТСЯ в состав символов найденной подстроки, в отличие от обычной группы - скобки вида ( некие символы ).

Таким образом, условие в скобках данной ретроспективной проверки, с учетом всего вышесказанного, обозначает следующее: подстрока (в текстовой строке), которая является регулярным выражением, МОЖЕТ (но необязательно) начинаться с символов переноса строк, после них ДОЛЖЕН идти один из разделителей; или ДОЛЖНО быть начало анализируемой текстовой строки. Сами же эти разделители НЕ БУДУТ включены в состав найденных символов.

Стоит отметить, что просмотры назад или вперед в регулярных выражениях требуют фиксированного числа символов. Т.е. там не применимы идентификаторы вида * (0 или более символов), + (1 или более символов), ? (0 или 1 символ), а также что-то вроде {1,4} (от 1 до 4 символов включительно). Т.е. это - удобная возможность технологии РВ, но с ограничениями.

2.3. Скобки (?:\s*?) обозначает обычную группу, без просмотров вперед или назад. Левая скобка (?: (три символа) устанавливает тот факт, что данная группа НЕ БУДЕТ включаться в перечень найденных групп, т.е. на нее не будет затрачиваться память. Так делается для экономии ресурсов компьютера и, соответственно, для ускорения поиска. Т.е. будет проверяться лишь наличие символов, заданных с этих скобках, сами же эти символы не будут включены в состав найденных. Их невозможно будет использовать для вывода на экран, замены и т.д.

Внутри этих скобок находятся символы \s*?. Это означает: пробельные символы, ноль или более, по возможности - минимум. Последнее обозначается знаком вопроса ?. Который в данном случае, по идее, необязателен.

2.4. Дальше идет обычная группа (группирующие скобки) (/[^*][^/\n]+?/[gimuys]*). здесь девая скобка состоит лишь из одного символа (. Символы текстовой строки, совпавшие с указанным скобках шаблоном, БУДУТ включены в состав найденных.

В начале видим символ слеша. Он, как читатель уже догадался, обозначает левую границу регулярного выражения, присутствующего в текстовой строке. После него идет [^*], что означает один любой символ, за исключением символа *

Напомним, что в квадратных скобках символ * является не идентификатором, а обычным символом (т.е. звездочкой). Тот факт, что первым символом в квадратных скобках идет ^, означает отрицание, т.е. "за исключением далее указанных символов".

Совокупность [^/\n]+? обозначает все символы, за исключением переноса строки \n; и их может присутствовать от одного и более (т.к. - идентификатор +). При этом число таких символов должно быть минимально возможным (что обозначается символом ?).

Затем идет второй ограничивающий слеш /, обозначающий конец регулярного выражения.

Для чего нужно отрицание звездочек между слешами? Это для того, чтобы отличить регулярное выражение от комментария. Т.е. чтобы отличить 

/ ... /  (регулярное выражение)

от

/* ... */ (комментарий).

Далее идет совокупность [gimuys]*. Она обозначает ноль (отсутствие) или более символов, перечисленных в квадратных скобках. Это - возможные флаги РВ. Их мы здесь рассматривать не будем, вы можете прочитать об этом где-нибудь.

2.5. Группа в скобках (?=$|[\s.,);}\]]) обозначает позитивный просмотр вперед. Т.е. один из символов, задаваемых этой группой, ДОЛЖЕН присутствовать после флагов. Но, сам он НЕ БУДЕТ включаться в состав найденных символов. Как видим, это - либо конец строки, обозначаемый символом доллара $. Это на случай, если искомое РВ будет идти самым последним в текстовой строке, если после него никаких других символов уже нет.

А то, что идет в квадратных скобках, это те самые разделители, которые ДОЛЖНЫ присутствовать после РВ, если не конец строки. Нам требуется обнаружить лишь один из таких символов, что идет после него - не суть важно.

Вот и всё. Надеемся, объяснение было достаточно подробным даже для начинающих.

В заключение

Таким образом, при помощи этого регулярного выражения можно найти регулярные выражения в текстовой строке, являющейся, например, программным кодом на языке javascript. Или же отрывком такого кода.

Стоит отметить, что специалисты высокого класса (речь не идет, разумеется, о тех, кто ограничивается разного рода фреймворками, библиотеками и иными готовыми решениями) утверждают, что для корректного разбора (распарсивания) программного кода, в том числе, на языке JS, не следует примерять регулярные выражения. Что следует разбивать текстовую строку на токены, затем проводить синтаксический анализ.

Вероятно, это так в самом деле. Однако, видится, что для частной задачи, только для того, чтобы найти в (корректном) программном коде JS хотя бы начала (начальные позиции и символы) регулярных выражений, вполне можно воспользоваться данным способом. Тем более, если речь идет о каком-то простом случае.

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


Комментарии:
Всего комментариев:0
Пожалуйста, не забудьте ознакомиться с правилами оставления комментариев.



Подписаться на комментарии на этой странице

Мы можем выполнить

Другие услуги
Интересная и полезная
информация
НАПИШИТЕ НАМ
Яндекс.Метрика
Номер телефона
© Copyright Все права защищены 2013-2026 Научный консалтинг