Классы символов в регулярных выражениях Java
Предопределённые и пользовательские классы символов в Java regex — \d, \w, \s, диапазоны, отрицания и пересечения.
Класс символов — это часть регулярного выражения, которая сопоставляется с одним символом из набора допустимых символов. Когда вы пишете \d, [aeiou] или [^0-9], вы используете класс символов. Это наименьший строительный блок почти любого полезного шаблона, и движок java.util.regex предоставляет три вида: готовые сокращения вроде \d, пользовательские наборы в виде [...] и классы с именами на основе Unicode вроде \p{Lower}. Освоив их, вы разберётесь и с остальным синтаксисом регулярных выражений.
В этой главе рассматриваются все три вида — пользовательские классы в квадратных скобках с диапазонами и отрицанием, предопределённые сокращения, операции над множествами (объединение, пересечение, вычитание) и именованные классы Unicode/POSIX — и всё это объединяется в одной запускаемой программе. Если вы новичок в Java regex, сначала изучите введение в regex и синтаксис regex.
Пользовательские классы: скобки, диапазоны и отрицание
Класс в квадратных скобках [...] соответствует любому одному символу, перечисленному внутри. Укажите символы по одному или используйте дефис для обозначения диапазона. Поставьте ^ сразу после открывающей скобки, чтобы отрицать набор — сопоставляться с любым символом, которого нет в списке.
"[abc]" // matches one 'a', 'b', or 'c'
"[a-z]" // any one lowercase letter (a range)
"[A-Za-z0-9]"// any letter or digit (three ranges in one class)
"[^aeiou]" // any single character that is NOT a vowelВнутри класса большинство метасимволов теряют своё специальное значение, поэтому их редко нужно экранировать. Исключение составляют литеральные ], ^, - и \ — их следует экранировать или размещать так, чтобы они не были поняты неверно (дефис - в начале или конце является литеральным дефисом, а не диапазоном).
java.util.regex.Pattern p = java.util.regex.Pattern.compile("[-+0-9]");
System.out.println(p.matcher("-").find()); // true: leading - is literal
System.out.println(p.matcher("*").find()); // falseПредопределённые классы: сокращения
Java предоставляет сокращения для наиболее часто используемых наборов. Каждая форма в нижнем регистре имеет дополнение в верхнем регистре, которое соответствует всему, чему не соответствует версия в нижнем регистре.
| Сокращение | Соответствует | Эквивалентный класс |
|---|---|---|
. | любой символ, кроме признаков конца строки | — |
\d | цифра | [0-9] |
\D | не цифра | [^0-9] |
\w | символ слова | [a-zA-Z_0-9] |
\W | не символ слова | [^a-zA-Z_0-9] |
\s | пробельный символ | [ \t\n\x0B\f\r] |
\S | не пробельный символ | [^\s] |
\d записывается как "\\d" в строковом литерале, а \w становится "\\w". Забыть вторую обратную косую черту — самая распространённая ошибка в Java regex: "\d" даже не скомпилируется.String digits = "\\d+"; // regex is \d+ (one or more digits)
String word = "\\w+"; // regex is \w+
"abc123".replaceAll("\\d", "#"); // "abc###"Отрицание, пересечение и объединение
Класс в квадратных скобках по умолчанию является объединением — укажите [abcxyz] и получите совпадение с любым из шести символов. Java добавляет два оператора для работы с множествами внутри классов. Оператор && формирует пересечение (соответствует только символам, входящим в оба набора), а вложение одного класса в другой позволяет выполнять вычитание.
| Конструкция | Значение |
|---|---|
[a-d[m-p]] | объединение: a–d или m–p |
[a-z&&[aeiou]] | пересечение: строчные буквы, являющиеся гласными |
[a-z&&[^aeiou]] | вычитание: строчные буквы, которые не являются гласными (согласные) |
"[a-z&&[^aeiou]]" // all lowercase consonants
"[\\p{L}&&[^\\p{Lu}]]" // any letter that is not uppercaseОтрицание с ^ инвертирует весь класс; пересечение с && сужает его. Правильный выбор между ними делает шаблоны читаемыми, вместо того чтобы нагромождать альтернации.
Именованные классы Unicode и POSIX
Для всего, что выходит за рамки ASCII, используйте семейство \p{...}. Эти именованные классы знают о категориях Unicode и группах в стиле POSIX, а \P{...} — их отрицательная форма. Они незаменимы, когда входные данные содержат буквы с диакритическими знаками, не латинские алфавиты, или когда вы просто хотите использовать говорящие имена.
| Класс | Соответствует |
|---|---|
\p{Lower} | строчная буква ASCII ([a-z]) |
\p{Punct} | знак пунктуации |
\p{Alnum} | буква ASCII или цифра |
\p{L} | любая буква Unicode (с UNICODE_CHARACTER_CLASS) |
\p{IsDigit} | цифра Unicode |
// Make \w, \d, \s honor full Unicode, not just ASCII:
java.util.regex.Pattern uni =
java.util.regex.Pattern.compile("\\w+", java.util.regex.Pattern.UNICODE_CHARACTER_CLASS);
System.out.println(uni.matcher("café").results().count()); // 1: "café" is one wordРазбор примера: все виды классов на одной строке
Эта программа создаёт небольшой вспомогательный метод count, который сообщает, сколько символов строки-образца совпадает с классом из одного символа. Запуск одной строки через классы цифр, символов слова, пробельных символов, диапазон, отрицание, пересечение, точку и класс POSIX делает взаимосвязи между ними наглядными — и показывает класс, выполняющий реальную работу внутри более широкого шаблона.
Что важно вынести из запуска:
\dи[0-9]оба сообщают 13 для этой строки — они полностью эквивалентны для ASCII-ввода. Предопределённый класс — это просто встроенное имя для набора, который вы могли бы записать вручную, поэтому используйте сокращение для удобства чтения.\Dи[^0-9]оба сообщают 30 — дополнение 13 цифр в строке из 43 символов (13 + 30 = 43). Сокращение в верхнем регистре и отрицательный класс в квадратных скобках — это два способа записи одного и того же дополнения.[a-z&&[aeiou]]сообщает 3 — только строчные гласные:eв Order,oв cost иiв ship-by. ЗаглавнаяOв Order — это буква в верхнем регистре, поэтому пересечение её исключает. Суть:&&сужает класс до символов, входящих в оба набора, а не объединяет их, поэтому заглавные гласные не входят.- Точка совпала с 43 символами, включая пробелы и знаки пунктуации, поскольку в этой однострочной строке нет признака конца строки, на котором
.остановилась бы. Точка — самый широкий из всех классов, именно поэтому её обычно заменяют более узким. - Шаблон
[A-Z]\dнашёлA7: классы символов используются не только для подсчёта — объединённые в последовательность, они проверяют структуру; здесь — букву, за которой непосредственно следует цифра.\p{Punct}отдельно нашёл 9 знаков пунктуации, демонстрируя, что именованные классы POSIX работают наряду с сокращениями.
Практика
Что изучать дальше
Класс символов соответствует одному символу; следующий шаг — управление тем, сколько их вы сопоставляете и что делать с результатом:
- Квантификаторы regex — добавьте
+,*,?и{n,m}, чтобы класс совпадал с последовательностью символов. - Захватывающие группы — оберните класс в
(...), чтобы извлечь найденную подстроку. - Pattern и Matcher — API, используемый во всех примерах выше, подробно.
- Флаги regex —
CASE_INSENSITIVE,UNICODE_CHARACTER_CLASSи другие параметры, изменяющие поведение классов.