Регулярные выражения
Regex описывает шаблон текста. Он удобен для локальной структуры строки, но плохо заменяет parser вложенного языка: JSON, HTML или SQL лучше разбирать специализированным инструментом.
Java API
Pattern orderPattern = Pattern.compile(
"^ORD-(?<year>\\d{4})-(?<number>\\d{6})$"
);
Matcher matcher = orderPattern.matcher("ORD-2026-000042");
if (matcher.matches()) {
String year = matcher.group("year");
String number = matcher.group("number");
}В Java matches() требует совпадения всей последовательности, find() ищет следующий фрагмент, lookingAt() — совпадение от начала. Обратный slash сначала обрабатывает Java string literal, поэтому regex \d записывается "\\d".
Основные конструкции
| Конструкция | Значение |
|---|---|
. | любой символ, с учётом flags |
\d, \w, \s | класс цифр, word, whitespace |
[...], [^...] | класс и отрицание |
*, +, ?, {m,n} | квантификаторы |
(...), (?:...) | capturing и non-capturing group |
^, $ | границы строки/line в зависимости от mode |
(?=...), (?<=...) | lookahead и lookbehind |
Greedy .* берёт максимально возможный фрагмент, reluctant .*? — минимальный, possessive .*+ не отдаёт символы при backtracking.
Безопасное динамическое значение
Если пользовательский текст должен быть literal, его нельзя вставлять в regex напрямую:
Pattern p = Pattern.compile(
"^" + Pattern.quote(userPrefix) + "\\d+$"
);Для replacement используется отдельная escaping-модель; динамическую replacement-строку защищает Matcher.quoteReplacement.
Поиск одиночного переноса внутри абзаца
(?<=\S)\R(?=\S)Он находит line break между непробельными символами. Перед массовой заменой проверяю sample: перенос после точки и перенос в code block могут иметь разный смысл. Для Markdown лучше ограничивать область обычными абзацами, а не применять pattern ко всему vault.
Производительность
Backtracking на шаблонах вроде (a+)+$ и длинной почти подходящей строке может расти взрывно. Защита:
- ограничить длину input;
- избегать вложенных неоднозначных quantifiers;
- применять atomic/possessive groups осознанно;
- компилировать постоянный
Patternодин раз; - измерять worst-case, если input недоверенный.
Типичные сбои
- Использовать
matches(), ожидая поиск подстроки. - Забыть двойное escaping в Java.
- Передать пользовательский fragment как готовый regex.
- Парсить вложенную структуру одним огромным pattern.
- Получить catastrophic backtracking на почти совпавшей строке.
- Считать
\wточным определением допустимого логина.
Вопросы для собеседования
Чем matches отличается от find? Первый сопоставляет весь input, второй ищет следующий фрагмент.
Зачем non-capturing group (?:...)? Группировать без сохранения отдельного результата.
Почему в Java пишут "\\d+"? Один backslash нужен Java literal, второй остаётся regex engine.
Что такое greedy quantifier? Он сначала захватывает максимально возможный фрагмент, затем отдаёт часть при backtracking.
Как вставить literal в pattern? Через Pattern.quote.