Рубрики
Без рубрики

Сила регулярных выражений

введение в базовые регулярные выражения с помощью Groovy. Помеченный регулярными выражениями, синтаксическим анализом, groovy, java.

Регулярные выражения (или regex ) – это концепция, которая относительно проста в освоении, но может оказать огромное влияние на читаемость, удобство обслуживания и производительность вашего кода. Все основные языки программирования поддерживают регулярные выражения, но Groovy, язык виртуальной машины Java (JVM) , по-видимому, обеспечивает наиболее элегантную реализацию, поэтому я буду использовать Groovy для этого руководства. Помните, что в жизни нет ничего простого, поэтому существует множество различных вариантов регулярных выражений (или “вкусов”) с поддержкой различных функций. Я постараюсь придерживаться вещей, которые являются общими для всех вкусов, но я отмечу, когда это не так.

Регулярное выражение – это последовательность символов, которая определяет шаблон. Этот шаблон можно искать в других последовательностях символов (или строках). Регулярное выражение может быть таким же простым, как

groovy:000> stringToSearch = "A string that contains the letter 'z'."
===> A string that contains the letter 'z'.

groovy:000> thingToFind = stringToSearch =~ /z/
===> java.util.regex.Matcher[pattern=z region=0,38 lastmatch=]

groovy:000> thingToFind.find()
===> true

groovy:000> thingToFind.start()
===> 35

groovy:000> stringToSearch[35]
===> z

Здесь я использую оболочку Groovy , которая предоставляется бесплатно, когда вы устанавливаете Groovy с помощью SDKMAN! . Обратите внимание, что строка, внутри которой мы ищем, представляет собой обычную строку, заключенную в кавычки ( " ). Шаблон регулярного выражения также является строкой, но он окружен косыми чертами ( / ). Это известно как “косая строка” . В строке с косой чертой единственным специальным символом, который необходимо экранировать, является сама буквальная косая черта, которая экранируется обратной косой чертой ( \/ ). Это шаблон, специфичный для Groovy. Большинство других языков (например, Java) требуют, чтобы вы экранировали все специальные символы в строках регулярных выражений, и не существует специальных строк с косой чертой для использования с регулярными выражениями.

Groovy использует java.util.regex. Соответствует в качестве движка регулярных выражений, потому что это язык на основе JVM, построенный поверх Java. Но Groovy также предоставляет специальный оператор, называемый оператором find , =~ , который определяет шаблон для сопоставления и сопоставляет его с предоставленной строкой.

Сопоставление подстрок внутри строк поддерживается java.lang. Сопоставление подстрок внутри строк поддерживается java.lang.

groovy:000> substring = "in"
===> in

groovy:000> stringToSearch.indexOf(substring)
===> 5

groovy:000> stringToSearch[5..6]
===> in

Groovy поддерживает нарезку массива (возврат выбранных диапазонов элементов массива) с помощью оператора индекса [a..b] . Если есть несколько совпадений, мы можем начать наш второй поиск после первого совпадения и так далее для дополнительных совпадений:

groovy:000> stringToSearch.indexOf(substring, 6)
===> 19

groovy:000> stringToSearch[19..20]
===> in

Эта функциональность, конечно, может быть воспроизведена с помощью регулярных выражений:

groovy:000> anotherThing = stringToSearch =~ /in/
===> java.util.regex.Matcher[pattern=in region=0,38 lastmatch=]

groovy:000> anotherThing.find()
===> true

groovy:000> anotherThing.start()
===> 5

groovy:000> anotherThing.find()
===> true

groovy:000> anotherThing.start()
===> 19

groovy:000> anotherThing.find()
===> false

groovy:000> anotherThing.start()
ERROR java.lang.IllegalStateException:
No match available
        at java_util_regex_MatchResult$start$1.call (Unknown Source)

find() возвращает true если дополнительное (или первое) совпадение может быть найдено, а затем start() возвращает индекс этого совпадения. Если find() не удается найти совпадение, он возвращает false и попытка запустить start() вернет ошибку.

Приведенные выше примеры хороши, если вы хотите найти только определенный символ или подстроку в заданном фрагменте текста. Но что, если вы хотите сопоставить что-то более сложное? Номера телефонов? Адреса? Как насчет сопоставления URL-адресов или проверки адресов электронной почты? Чтобы описать что-либо более сложное, чем простые строки буквенных символов, нам нужно ввести то, что известно как метасимволы .

На протяжении всего этого руководства я рекомендую вам следовать этому, попробовав эти регулярные выражения в На протяжении всего этого руководства я рекомендую вам следовать этому, попробовав эти регулярные выражения в .

Основные метасимволы

Метасимволы – это, как следует из их названия, символы, которые имеют дополнительное значение помимо того, что они буквально представляют. Значение метасимвола зависит от его контекста. Вы наверняка сталкивались с метасимволами раньше, возможно, даже не осознавая этого. Точка (или “точка” или “точка”) в конце предыдущего предложения является своего рода метасимволом. В этом контексте точка указывает на конец этого конкретного предложения, но ее также можно использовать, например, в качестве десятичной точки в числе с плавающей запятой или разделителя в таких вещах, как номера телефонов (800.555.1234) и веб-адреса (timecube.2enp.com ).

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

. == any single character

Так, например, если вы хотите написать регулярное выражение, которое соответствовало бы словам “cat” и “cut”, вы могли бы написать:

c.t

…но . соответствует любому символу, поэтому это выражение также будет соответствовать “cot” или “c @ t” или “c? t” или ” c t” или что-нибудь еще. Чтобы ограничить совпадения, вы можете попробовать использовать выражение в квадратных скобках:

[abc] == matches a OR b OR c

Квадратные скобки – это метасимволы. Любые символы в квадратных скобках интерпретируются буквально (так что . интерпретируется как буквальный символ полной остановки, а не как “любой отдельный символ”). Выражения, заключенные в квадратные скобки, позволяют сопоставлять более конкретные случаи, чем полная остановка. Например, мы можем настроить наше предыдущее регулярное выражение так, чтобы только соответствовало словам “cat” и “cut” следующим образом:

c[au]t

Символ [au] будет соответствовать либо одному символу “a”, либо одному символу “u”, но не обоим (поэтому “caut”, “caat”, “cuut” и т.д. Не совпадают). Приведенное выше регулярное выражение соответствует словам “cat” и “cut” и только словам “cat” и “cut”. Что, если мы хотим исключить результаты через? Скажем, мы хотим соответствовать c.t за исключением , когда слово “кошка” или “вырезать”. Что ж, мы можем использовать метасимвол carat в выражении в квадратных скобках:

c[^au]t == matches any string c.t except "cat" and "cut"

Приведенное выше соответствует любой трехсимвольной строке, которая начинается с ‘c’ и заканчивается на ‘t’, при условии, что средний символ не ‘a’ или ‘u’. Метасимвол carat выполняет двойную функцию: когда он появляется за пределами выражения в квадратных скобках, он означает “начало строки” или, для приложений на основе строк, “начало строки”.:

^[ ] == matches a single space at the beginning of a string / line

Таким образом, это соответствовало бы начальному пробелу в строке " hello" , но не соответствовало бы "hello world" , потому что, несмотря на то, что в этой строке есть пробел, он не отображается в начале строки. Аналогичный символ используется для сопоставления end строки или строки, знака доллара:

[.!?]$ == matches a period, exclamation point, or question mark at the end of a string / line

Знак доллара в выражении, заключенном в квадратные скобки, интерпретируется буквально как символ знака доллара. Когда он находится за пределами выражения, заключенного в квадратные скобки, он интерпретируется как “конец строки/строки”. Оглядываясь назад на карат, можно сказать, что это был метасимвол как внутри, так и за пределами выражений в скобках, не так ли? Итак, как мы можем буквально сопоставить карат? Мы можем экранировать символ, используя обратную косую черту:

\^ == matches the carat character literally

…или, проще говоря, просто не помещая его в левую часть выражения в скобках:

[a^b] == matches any of the characters 'a', '^', or 'b'

Обратная косая черта используется для определения escape-последовательностей , Которые могут включать представления символов в юникоде иностранных алфавитов, эмодзи и т. Д., Но обычно используются для непечатаемых символов, таких как табуляция и разрывы строк, а также для экранирования специальных символов, таких как карат. Некоторые распространенные escape-последовательности включают:

\n == matches a newline / line break / line feed
\r == matches a carriage return
\t == matches a tab character

Помните, что DOS использует возврат каретки и перевод строки ( \ r \ n ) в качестве индикатора “конца строки”, в то время как Unix использует только перевод строки ( \ n ), поэтому ваши текстовые документы, перенесенные с Mac на Windows, иногда могут потерять все свои разрывы строк. Большинство символов большую часть времени интерпретируются буквально, но вы можете принудительно интерпретировать буквальный метасимвол any , поставив перед ним обратную косую черту. Например:

\. == matches a literal full stop character
\\ == matches a literal backslash character
\[ == matches a literal opening bracket character
\] == matches a literal closing bracket character

…..и так далее. Обратите внимание, что это не относится к буквенным символам (мы видели выше, что \t соответствует символу табуляции, например, а не буквальному символу ‘t’). Как правило, буквенно-цифровые символы интерпретируются буквально, в то время как большинство других символов имеют некоторую метаинтерпретацию. Но эти символы могут быть истолкованы буквально, если они включены в выражение в квадратных скобках:

[.^$] == matches any of the characters '.', '^', or '$'

Единственными другими исключениями из этого правила являются случаи, когда символ карата является первым символом в выражении в квадратных скобках, как упоминалось ранее, символ обратной косой черты и сами символы квадратных скобок, которые должны быть экранированы:

[\[\]\\] == matches any of the characters '[', ']', or '\'

Последним базовым метасимволом является символ “чередование” (он же. “выбор” или “установить объединение”), который является тем же символом, что и Unix “pipe”, | . Символ выбора позволяет указать два альтернативных совпадения, по одному с каждой стороны вертикальной полосы. Итак, если вы хотите сопоставить чье-то имя или их псевдоним, вы могли бы сделать что-то вроде:

Robert|Bob == matches "Robert" or "Bob", but not both

Вы можете комбинировать варианты выбора и скобки для более сложных совпадений:

[BR]ob|Ric[hk]

Вышеприведенное соответствует любому из имен “Боб”, “Роб”, “Рич” или “Рик”.

Кванторы

С точкой . , скобки [] , карат ^ , чередование | , и знак доллара $ , мы можем сопоставлять определенные символы, предоставлять группы приемлемых и неприемлемых символов, альтернативные совпадения и указывать, что эти совпадения должны происходить в начале или конце строк/строк. С обратной косой чертой \ , у нас есть возможность указать любой символ, который следует рассматривать как часть нашего соответствия, с помощью escape-последовательностей. Но что, если мы хотим увидеть персонажа определенное количество раз? Прямо сейчас у нас нет возможности указать это, но звезда Kleene (также называемая просто “звездой” или “глобусом”) позволяет нам сопоставлять ноль или более предыдущего символа, например:

lol* == matches "lo", "lol", "loll", "lolll", etc.

Мы могли бы использовать приведенное выше регулярное выражение, чтобы увидеть, насколько смешными нас кто-то считает, что, как мы все знаем, напрямую связано с количеством букв “l” в конце “lol”. Но вышеприведенное также соответствует “lo” (заставляя нас звучать так, как будто мы собираемся сообщить какие-то плохие, старые новости), потому что l * в конце означает, что нулевые конечные буквы “l” приемлемы. Чтобы обойти это (и указать, что нам нужна хотя бы одна буква “l” в конце), мы могли бы написать:

loll* == matches "lol", "loll", "lolll", etc.

Спецификация расширенного регулярного выражения POSIX предоставляет для этого ярлык – метасимвол + , который означает “один или несколько” предыдущего символа. Таким образом, вышесказанное эквивалентно:

lol+ == matches "lol", "loll", "lolll", etc.

Если мы думаем, что люди неискренни со своими lol, и мы хотим принять только стандартное “lol” и немного более восторженное “lol”, нам может понадобиться только одна или две буквы “l” в конце. Расширенное регулярное выражение POSIX предоставляет еще одно сокращение, которое принимает только ноль или одну копию предыдущего символа, ? метасимвол:

loll? = matches only "lol" or "loll"

Что, если мы захотим отсеять льстецов и найти только тех, кто использовал более двух букв “л” в конце своего lol? Ну, мы могли бы снова использовать метасимвол + и написать что-то вроде:

lolll+ == matches "lolll", "lollll", "lolllll", etc.

…или мы могли бы использовать другой базовый метасимвол (который мы еще не представили), метасимволы фигурных скобок (или “фигурные скобки”), {} . Фигурные скобки можно использовать для указания точно сколько вхождений предыдущего символа вы хотите видеть (при использовании типа {a} , где a является некоторым неотрицательным целым числом); минимальное количество допустимых вхождений (при использовании типа {a,} ); или как минимальное, так и максимальное количество допустимых вхождений (при использовании типа {a,b} , где оба a и b являются неотрицательными целыми числами и b > a ). Приведенное выше регулярное выражение затем будет переписано как:

lol{3,} == matches "lolll", "lollll", "lolllll", etc.

Мы могли бы дополнительно разделить эти совпадения, используя фигурные скобки несколькими способами:

lol{3}   == matches "lolll" only
lol{4,6} == matches "lollll", "lolllll", or "lollllll"
lol{7,}  == matches "lolllllll", "lollllllll", and so on

Это откровенно огорчительное количество букв “л”. Давайте вычеркнем их из нашей жизни, узнав о ленивом и собственническом совпадении.

Ленивый против Жадное и Собственническое соответствие

Когда мы узнали о метасимволе + несколько абзацев назад, я сказал, что он соответствует “одному или нескольким” вхождениям предыдущего символа. Но как это регулярное выражение “решает”, скольким вхождениям соответствовать? Например, соответствует ли регулярное выражение ” loll+ ” строке ” lolllll/| " "/| lol ” или ” lolll/| " или " lollll " или что?

В Python, Java и некоторых других движках регулярных выражений кванторы + , * , и ? являются жадными по умолчанию. Они захватывают как можно больше символов, если это не приводит к сбою сопоставления. Таким образом, ответ на приведенный выше вопрос заключается в том, что ” loll + ” будет соответствовать всей строке ” lolllll/| ". Определитель "до тех пор, пока это не приведет к неудачному совпадению", лучше всего иллюстрируется примером. Рассмотрим строку:

while (a < b) { while (c < d) { --d;}; while (b < c) { ++a; --c; } }

Если вы пишете свой собственный синтаксический маркер или компилятор, вам может потребоваться проанализировать строку, подобную этой. Большинство современных редакторов предоставляют “сопоставление скобок”, где, когда пользователь наводит курсор на открывающую скобку { , выделяется соответствующая ей закрывающая скобка } . Если вы хотите, чтобы простое регулярное выражение для этого находило открывающую скобку, все, что между ними, а затем закрывающую скобку, вы могли бы написать что-то вроде:

{.*}

Это должно соответствовать открывающей скобке { , за которой следуют любые символы . любое количество раз * , за которым следует закрывающая скобка, верно? Ну, по умолчанию, да, это так. Это то, чему будет соответствовать приведенное выше регулярное выражение в этой строке кода:

              { while (c < d) { --d;}; while (b < c) { ++a; --c; } }
              {....................................................}

(Начальный пробел добавлен для удобства визуального сравнения с исходной строкой.) Я показал, какие символы соответствуют символу . добавив строку “легенда” под полным совпадением. The . * захватывает как можно больше символов, если это не приводит к сбою сопоставления. Мы можем сделать * притяжательным, добавив + после этого, вот так:

{.*+}

.*+ теперь будет захватывать как можно больше символов, даже если это приведет к сбою сопоставления, что в нашем примере и происходит:

              { while (c < d) { --d;}; while (b < c) { ++a; --c; } }
              {.....................................................

.* + теперь фиксирует даже последний символ } , и регулярное выражение не совпадает, потому что в .*+ “съел” } , на котором он должен был закончиться. Это притяжательное соответствие. Напротив, ленивое сопоставление может быть принудительно выполнено путем добавления ? вместо того, чтобы a + после квантора. Так , для вышеизложенного, если мы переписали наше регулярное выражение следующим образом:

{.*?}

The . *? теперь будет захватывать как можно меньше символов, при условии, что это не приведет к сбою сопоставления:

              { while (c < d) { --d;}
              {.....................}

Регулярное выражение, похоже, соответствует самой первой закрывающей скобке, даже если после нее больше символов. Это ленивое сопоставление. Ленивое сопоставление не очень полезно в этом контексте, где блоки кода могут находиться внутри других блоков кода, но оно полезно для таких вещей, как синтаксический анализ текста, который может содержать выражения в кавычках. Кавычки не могут существовать внутри других кавычек, поэтому ленивое сопоставление (поиск следующего " после того, как мы “открыли” цитату с первым " ) – это правильный путь. Обратите внимание, что это также относится к таким вещам, как комментарии XML и HTML, которые не могут быть вложенными.

Диапазоны символов и классы

Предоставлять множество и множество альтернативных персонажей может быть утомительно. Предположим, мы хотим сопоставить почтовый индекс в американском стиле, который состоит всего из пяти последовательных цифр. Чтобы сделать это с тем, что мы написали до сих пор, мы могли бы написать:

[0123456789][0123456789][0123456789][0123456789][0123456789]

…или, более компактно

[0123456789]{5} == matches any 5-digit number from 00000 to 99999

Но есть еще более компактный способ записать это с помощью регулярных выражений, используя ranges . Диапазоны задаются в выражениях в квадратных скобках с помощью символа - . Например, вместо [0123456789]{5} , мы можем написать просто

[0-9]{5} == matches any 5-digit number from 00000 to 99999

Приведенное выше регулярное выражение выполняет то же самое, что и предыдущее регулярное выражение, но гораздо более кратко. Диапазоны экономят еще больше места, когда они используются с буквенными символами:

[A-Z] == matches any uppercase letter 'A' through 'Z' ("upper")
[a-z] == matches any lowercase letter 'a' through 'z' ("lower")

Диапазоны могут быть объединены и обрезаны, а также объединены символами, не входящими в диапазон, в скобках, поэтому мы можем определять такие вещи, как:

[A-Za-z0-9]   == matches any alphanumeric ("alnum") character ("alpha" + "digit")
[A-Za-z0-9_]  == matches any "word" character ("alnum" + '_')
[A-Fa-f0-9]   == matches any hexadecimal digit ("xdigit")

…..и так далее. Другие распространенные классы символов включают:

[A-Za-z]      == matches any alphabetic ("alpha") character ("upper" + "lower")
[0-9]         == matches any numeric character ("digit")
[ \t\r\n\v\f] == matches any whitespace character ("space")

Любой из этих наборов также может быть отменен с помощью ^ . Например, мы можем получить все символы, которые не являются буквенно-цифровыми (“alnum”) с помощью:

[^A-Za-z0-9]  == matches any non-alphanumeric character

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

[:alpha:]  ==  POSIX "alpha" alias
\a         ==  Perl/Tcl "alpha" alias
\p{Alpha}  ==  Java "alpha" alias

Лучше не предполагать, что вы можете угадать эти псевдонимы, если только вы не планируете придерживаться одного механизма регулярных выражений всю свою карьеру. Он совместим с несколькими движками для записи [а-за-З] и на самом деле вводится меньше символов, чем любой из [:alpha:] или \p {Alpha} – иногда лучше просто придерживаться основ. (Если вы хотите получить краткий обзор некоторых распространенных классов персонажей, вы можете ознакомиться с ними здесь .)

Захват Групп

Последнее, что следует рассмотреть в этом обзоре основных регулярных выражений, – это идея группы захвата (также известной как “подвыражение” или просто “группа”). Подвыражение определяется набором круглых скобок () и может использоваться для группировки символов или предоставления нескольких “совпадений”. Например, мы могли бы захотеть сопоставить открывающий и закрывающий HTML-теги, чтобы убедиться, что все, что находится внутри открывающего тега, соответствует тому, что находится внутри закрывающего тега:

<(.+)>.*?

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

groovy:000> s = "thing"
===> thing

groovy:000> m = s =~ "<(.+)>.*?"
===> java.util.regex.Matcher[pattern=<(.+)>.*? region=0,12 lastmatch=]

groovy:000> m.find()
===> true

groovy:000> m.group(0)
===> thing

groovy:000> m.group(1)
===> b

groovy:000> m.group(2)
===> a

В 0 th группа – это полное совпадение выражений, в то время как последовательные группы являются совпадениями подвыражений. Наше регулярное выражение имеет два определенных подвыражения, поэтому они нумеруются как группы 1 и 2 . Мы могли бы запустить это регулярное выражение для наших HTML-тегов, чтобы убедиться, что открывающие теги соответствуют закрывающим тегам.

Или мы могли бы захотеть предоставить несколько альтернативных совпадений, объединив группы захвата, варианты и кванторы:

(\+1 )?(([(][0-9]{3}[)] )|([0-9]{3}[ .-]))[0-9]{3}[ .-][0-9]{4}

Приведенное выше регулярное выражение будет соответствовать любому номеру телефона в США в обычном формате. Например:

+1 555-234-1234   == matches
(654) 999-0234    == matches
+1 (101) 234 9838 == matches
333 444.5555      == matches

…одна странная вещь, которую допускает это регулярное выражение, заключается в том, что разделители между тремя группами чисел должны быть разными, например:

111.234-3463 == matches

Это выглядит немного необычно и может быть отфильтровано с помощью более сложной схемы проверки.

На группы захвата можно ссылаться позже в вашем регулярном выражении, если вам нужно повторить те же группы снова. Это экономит ввод текста и может уменьшить количество ошибок. Чтобы ссылаться на N -ю группу позже в регулярном выражении, просто используйте ярлык \N :

(shoo)(bee)(doo)(\2)

Приведенное выше регулярное выражение будет соответствовать строке “shoobeedoobee”. Следующее регулярное выражение будет соответствовать строке “doowopdoowop”, но не “doowop”.:

(doo)(wop)\1\2

Вы можете ссылаться только на девять групп захвата, подобных этой ( \ 1 через \9 ), потому что двух- или трехзначные числа, которым предшествует косая черта, интерпретируются как восьмеричные цифры или индексы символов ( \103 будет интерпретироваться как символ C , например).

Вот несколько примеров регулярных выражений, которые могут вас вдохновить! Присылайте свои собственные шаблоны ниже, если вам известны какие-либо полезные из них!

Адреса электронной почты

Сейчас я живу в Ирландии, но часто возвращаюсь в США, чтобы навестить семью и друзей. Поскольку я недостаточно просвещен, чтобы иметь телефон с двумя SIM-картами, мне приходится прибегать к подписке на бесплатный Wi-Fi в аэропортах и Starbucks. Многие из этих страниц входа в систему используют простые регулярные выражения для проверки адресов электронной почты, чтобы они могли спамить вас мусором или продавать вашу информацию, чтобы сделать эту милую, милую мулу. Действительно наивное регулярное выражение для проверки адресов электронной почты может выглядеть следующим образом:

.+@.+\..++

Это позволило бы захватить большинство адресов электронной почты, в том числе с окончаниями типа .co.uk , но это также позволяет использовать мусор, такой как a@b.c или bob@bob.bob (два моих любимых одноразовых письма). Если вы действительно хотите подтвердить адрес электронной почты, вы должны отправить электронное письмо с подтверждением на этот адрес и потребовать, чтобы пользователь нажал на ссылку или что-то в этом роде.

Общие числовые шаблоны

Разные языки программирования допускают разные виды представлений чисел. Некоторые языки позволяют ставить “f” или “F” после числа, чтобы указать, что оно должно интерпретироваться как число с плавающей запятой (а не как целое число с двойной точностью), или “l” или “L”, чтобы указать, что оно должно быть “длинным” (двойной ширины) целым числом. Другие языки позволяют использовать “e” или “E” для обозначения научных обозначений, разрешают вводные знаки “+” и так далее. Следующее регулярное выражение допускает самые разные виды числовых представлений (без завершающих букв “f” и “L”).:

[+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)?

Синтаксический анализ Кода

Следующее (чрезвычайно сложное) регулярное выражение анализирует сигнатуры методов для методов Java. Он соответствует любой допустимой сигнатуре метода Java (насколько я знаю!):

(?:(?:(public|protected|private)\s+)|(?:(abstract|static)\s+)|(?:(final)\s+)|(?:(volatile|synchronized)\s+)|(?:(native|strictfp)\s+))*([a-zA-Z_][[:alnum:]]+)\s+([a-zA-Z_][[:word:]<>\[\]]+)\s*\(\s*(?:(?:([a-zA-Z_][[:word:]<>\[\]]+)\s+([a-zA-Z_][[:alnum:]]+)\s*)(?:,\s*([a-zA-Z_][[:word:]<>\[\]]+)\s+([a-zA-Z_][[:alnum:]]+)\s*)*)?\)\s*\{

Оригинал: “https://dev.to/awwsmm/the-power-of-regular-expressions-30in”