W3docs

Синтаксис регулярных выражений Java

Синтаксис регулярных выражений Java — символы, классы, якоря, квантификаторы и специальные конструкции.

Регулярное выражение — это небольшой язык шаблонов для описания текста. Java реализует его в пакете java.util.regex, и синтаксис представляет собой диалект в стиле Perl, который используется большинством современных языков — с одной особенностью Java: каждый обратный слеш в шаблоне должен быть удвоен в строковом литерале Java, поскольку компилятор поглощает один обратный слеш до того, как его увидит движок регулярных выражений. Эта глава является справочником по данному синтаксису: строительные блоки, которые вы комбинируете для поиска, замены и проверки текста.

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

Литералы и метасимволы

Большинство символов в шаблоне соответствуют самим себе: cat соответствует трём буквам c, a, t. Мощь приходит от метасимволов — символов со специальным значением, которые вы комбинируете в правила. Двенадцать символов, которые движок обрабатывает особым образом:

. ^ $ * + ? ( ) [ ] { } | \

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

Pattern.matches("a.c", "abc");   // true  — '.' matches any char
Pattern.matches("a.c", "a.c");   // true  — '.' also matches a literal dot
Pattern.matches("a\\.c", "abc"); // false — '\.' matches ONLY a literal dot
Pattern.matches("a\\.c", "a.c"); // true

Классы символов

Класс символов в квадратных скобках соответствует любому одному символу из набора. Диапазоны используют дефис, а ведущий ^ инвертирует набор.

"[aeiou]"     // any one lowercase vowel
"[a-z]"       // any one lowercase letter
"[A-Za-z0-9]" // any letter or digit
"[^0-9]"      // any character that is NOT a digit

Java также предлагает предопределённые классы в качестве сокращений. Вот те, к которым вы будете обращаться постоянно:

СокращениеЭквивалентСоответствует
.Любой символ, кроме символа конца строки
\d[0-9]Цифра
\D[^0-9]Не цифра
\w[a-zA-Z0-9_]Символ слова
\W[^a-zA-Z0-9_]Не символ слова
\s[ \t\n\x0B\f\r]Пробельный символ
\S[^\s]Не пробельный символ

Форма в верхнем регистре всегда является отрицанием формы в нижнем регистре. См. классы символов Java Regex для полного набора, включая классы POSIX и Unicode.

Квантификаторы: жадные, ленивые, собственнические

Квантификатор указывает, сколько раз предшествующий элемент может повторяться. По умолчанию квантификаторы являются жадными — они захватывают как можно больше, а затем отступают, если остаток шаблона этого требует. Добавьте ?, чтобы сделать квантификатор ленивым (сопоставить как можно меньше), или +, чтобы сделать его собственническим (захватить и никогда не отдавать).

КвантификаторЗначение
*Ноль или более
+Один или более
?Ноль или один (необязательный)
{n}Ровно n
{n,}Не менее n
{n,m}От n до m
"\\d{3}"     // exactly three digits
"\\d{2,4}"   // two to four digits
"a+"         // one or more 'a'
"colou?r"    // matches "color" and "colour"
"<.+>"       // greedy:    on "<a><b>" matches the whole "<a><b>"
"<.+?>"      // reluctant: on "<a><b>" matches just "<a>"

Для более подробного рассмотрения жадного, ленивого и собственнического поведения прочитайте квантификаторы Java Regex.

Якоря, границы и чередование

Якоря соответствуют позиции, а не символу. ^ — начало ввода (или строки в многострочном режиме), $ — конец, а \b — граница слова, то есть позиция нулевой ширины между \w и \W. Чередование с | соответствует любой из сторон.

"^Hello"      // "Hello" only at the start
"\\.txt$"     // ".txt" only at the end
"\\bcat\\b"   // "cat" as a whole word, not inside "category"
"cat|dog"     // "cat" or "dog"
"^(cat|dog)$" // the whole string is exactly "cat" or "dog"

Обратите внимание, что | имеет очень низкий приоритет: ^cat|dog$ означает (^cat)|(dog$), а не ^(cat|dog)$. Оборачивайте альтернативы в группу, если хотите, чтобы якоря применялись к обеим.

Группы, обратные ссылки и встроенные флаги

Скобки создают захватывающую группу — движок запоминает, что совпало с каждой группой, нумеруя их слева направо, начиная с 1. (?:...) — это незахватывающая группа, когда нужно только применить квантификатор. Обратная ссылка \1 соответствует тому же тексту, который захватила первая группа. Встроенные флаги вида (?i) изменяют поведение сопоставления без отдельного флага Pattern.compile.

"(\\d{4})-(\\d{2})"   // group 1 = year, group 2 = month
"(?:ab)+"             // repeats "ab" without capturing it
"(\\w+) \\1"          // a word followed by itself ("the the")
"(?i)java"            // case-insensitive: matches "Java", "JAVA"
"(?m)^line"           // multiline: ^ matches at each line start

Захватывающие группы имеют собственную главу — группы Java Regex охватывает именованные группы и способы извлечения захваченного текста из Matcher. Встроенные флаги (?i) и (?m) являются внутришаблонными эквивалентами флагов Pattern.compile, описанных в флагах Java Regex.

Практический пример: конструкции в действии

Эта программа демонстрирует использование класса цифр с квантификатором, заякоренного чередования, обратной ссылки, жадного и ленивого сопоставления, сокращения \w+ и встроенного флага без учёта регистра — всё с использованием только java.util.regex. Синтаксис здесь управляет API Pattern и Matcher, описанным в Pattern и Matcher Java Regex.

java— editable, runs on the server

Что можно вынести из выполнения:

  • \d{4} нашёл и 1995, и 2011, потому что find() сканирует каждое совпадение во входных данных, а комбинация класса и квантификатора (\d, повторённый {4} раза) — канонический способ сопоставить поле фиксированной ширины. Удвоенный обратный слеш в "\\d{4}" — это строковый литерал Java, дающий одинарный обратный слеш, который нужен движку.
  • Pattern.matches("cat|dog", "dog") вернул true, но тот же шаблон для "catnap" вернул falsematches() неявно привязывает весь ввод, поэтому, хотя cat появляется в catnap, оставшийся nap остаётся несопоставленным и общее совпадение не срабатывает.
  • Обратная ссылка \1 превратила (\w+) \1 в «слово, за которым следует то же самое слово», поэтому были найдены the и is — два повтора — а каждое слово, которое не повторялось сразу, было проигнорировано. Обратные ссылки соответствуют захваченному тексту, а не шаблону снова.
  • На одном и том же входе <a><b> жадный <.+> поглотил всю строку, тогда как ленивый <.+?> остановился на первом >, выдав только <a>. Этот простой контраст — самое распространённое исправление ошибок в регулярных выражениях: добавьте ? к квантификатору, когда он захватывает слишком много.
  • \w+ насчитал 3 токена в ab, cd-ef!ab, cd и ef — потому что ,, - и ! являются символами \W (не символами слова), которые прерывают последовательность символов слова. Встроенный флаг (?i) затем сопоставил java с JAVA, показав, что флаги могут находиться внутри самого шаблона, а не только в Pattern.compile.

Практика

Практика
На входе '<a><b>', почему регулярное выражение '<.+>' соответствует всей строке '<a><b>', а '<.+?>' — только '<a>'?
На входе '<a><b>', почему регулярное выражение '<.+>' соответствует всей строке '<a><b>', а '<.+?>' — только '<a>'?
Was this page helpful?