Синтаксис регулярных выражений 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 digitJava также предлагает предопределённые классы в качестве сокращений. Вот те, к которым вы будете обращаться постоянно:
| Сокращение | Эквивалент | Соответствует |
|---|---|---|
. | — | Любой символ, кроме символа конца строки |
\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.
Что можно вынести из выполнения:
\d{4}нашёл и1995, и2011, потому чтоfind()сканирует каждое совпадение во входных данных, а комбинация класса и квантификатора (\d, повторённый{4}раза) — канонический способ сопоставить поле фиксированной ширины. Удвоенный обратный слеш в"\\d{4}"— это строковый литерал Java, дающий одинарный обратный слеш, который нужен движку.Pattern.matches("cat|dog", "dog")вернулtrue, но тот же шаблон для"catnap"вернулfalse—matches()неявно привязывает весь ввод, поэтому, хотя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.