Matches: примеры (JAVA)
matches(String regex): booleanОбщее описание метода matches
В Java метод matches применяется для проверки соответствия строки регулярному выражению. Наиболее часто используется как:
String.matches(String regex)- метод класса java.lang.String;Pattern.matches(String regex, CharSequence input)- статический метод класса java.util.regex.Pattern;Matcher.matches()- метод экземпляра java.util.regex.Matcher.
Ключевая особенность: все перечисленные варианты проверяют соответствие всей входной строки шаблону. Для поиска подстроки служат другие методы, например Matcher.find().
Аргументы и возвращаемые значения:
String.matches(String regex): принимает один аргументregex(регулярное выражение в виде строки). Возвращаетboolean-true, если вся строка соответствует шаблону, иначеfalse. При неверном синтаксисе регулярного выражения выбрасываетсяPatternSyntaxException.Pattern.matches(String regex, CharSequence input): принимает шаблон и входную последовательность символов. ЭквивалентенPattern.compile(regex).matcher(input).matches(). Возвращаетboolean.Matcher.matches(): вызывается для уже скомпилированного шаблона (Pattern) и проверяет совпадение всего текста, возвращаетboolean. Полезен, когда требуется использовать флаги или повторно применять компилированный шаблон.
Флаги и модификаторы регулярных выражений применяются через Pattern.compile(regex, flags) или через встроенные модификаторы ((?i) для игнорирования регистра, (?s) для DOTALL и т. п.). Строковый метод String.matches не принимает флаги напрямую.
Особенности поведения:
- Метод проверяет полное совпадение, поэтому шаблон
\d+для строки"12ab"вернётfalse, а для"123"вернётtrue. - При необходимости частичного поиска стоит использовать
PatternиMatcher.find()или шаблон с явными подшаблонами, например.*pattern.*для приведения к полному совпадению.
Короткие примеры использования
Несколько простых примеров с кодом и ожидаемым результатом.
// Пример 1: String.matches
String s1 = "abc123";
boolean r1 = s1.matches("[a-z]+\\d+");
System.out.println(r1);
true
// Пример 2: Pattern.matches эквивалент
boolean r2 = java.util.regex.Pattern.matches("\\d+", "42");
System.out.println(r2);
true
// Пример 3: Matcher.matches против find
java.util.regex.Pattern p = java.util.regex.Pattern.compile("cat");
java.util.regex.Matcher m = p.matcher("concatenate");
System.out.println(m.matches());
System.out.println(m.find());
false true
// Пример 4: Флаги через Pattern.compile
java.util.regex.Pattern p2 = java.util.regex.Pattern.compile("hello", java.util.regex.Pattern.CASE_INSENSITIVE);
System.out.println(p2.matcher("HeLlO").matches());
true
Похожие инструменты в Java
В Java есть несколько похожих средств для работы с регулярными выражениями и их проверкой:
- Matcher.find() - ищет частичное совпадение внутри текста. Предпочтительнее при поиске подстроки.
- Matcher.lookingAt() - проверяет совпадение с начала строки, но не обязательно до конца. Полезно при проверке префикса.
- String.contains и indexOf - для простого поиска подстроки без регулярных выражений; работают быстрее и проще при точном совпадении последовательности символов.
- Pattern.compile(...) + matcher - предпочтительно при многократном использовании одного шаблона (экономия на компиляции) и при необходимости флагов.
Выбор зависит от задачи: для полного соответствия использовать matches, для поиска подстроки - find, для проверки префикса - lookingAt, для простого поиска без regex - contains/indexOf.
Альтернативы в других языках
Краткое сравнение с аналогами в популярных языках и отличия от Java:
- JavaScript:
/pattern/.test(str)илиRegExp.prototype.test. По умолчанию ищет соответствие подстроки. Для проверки полного совпадения требуется якорь ^...$.
/^\d+$/.test('123')true
- Python:
re.fullmatch(pattern, string)проверяет полное совпадение (аналог String.matches), аre.searchнаходит подстроку.
import re print(bool(re.fullmatch(r"\d+", "42")))True
- PHP:
preg_match('/pattern/', $str)возвращает числа совпадений; по умолчанию ищет подстроку, для полного совпадения нужен ^...$.
<? var_dump(preg_match('/^\\d+$/', '42')); ?>int(1)
- C#:
Regex.IsMatch(input, pattern)по умолчанию ищет подстроку; для полного соответствия добавить якоря или использовать ^...$.
using System.Text.RegularExpressions; Console.WriteLine(Regex.IsMatch("123", "^\\d+$"));True
- Go:
regexp.MatchString(pattern, s)ищет соответствие подстроки; для полного совпадения нужны якоря.
package main import ( "fmt" "regexp" ) func main(){ fmt.Println(regexp.MatchString("^\\\d+$", "42")) }true
- Kotlin:
String.matches(Regex)проверяет всю строку, аналогично Java String.matches. Также естьRegex.containsMatchInдля поиска подстроки.
println("abc".matches(Regex("[a-z]+")))true
- Lua:
string.matchиспользует упрощенные шаблоны Lua, отличающиеся от PCRE/Java regex; для сложных regex требуется внешняя библиотека. - SQL: разные СУБД предоставляют разные возможности:
LIKEдля простых шаблонов, в MySQL естьREGEXPдля регулярных выражений, но синтаксис и поведение могут отличаться от Java.
Главные отличия: в Java String.matches требует полного совпадения; в некоторых языках методы по умолчанию ищут подстроку. Также поддержка флагов и синтаксиса регулярных выражений варьируется.
Типичные ошибки при использовании
- Ожидание частичного совпадения при использовании
String.matches. Пример:System.out.println("hello world".matches("world"));false
Объяснение: метод требует совпадения всей строки; для поиска подстроки использовать.*world.*илиMatcher.find(). - Неправильное экранирование обратных слэшей в строковых литералах. Пример:
System.out.println("\"\\d+\"".matches("\\d+")); // неверное использованиеfalse
Правильно:s.matches("\\\d+"), где каждый обратный слэш в regex требует двойного экранирования в Java-строке. - Игнорирование исключений синтаксиса шаблона. Пример:
String regex = "([0-9]"; System.out.println("123".matches(regex));Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed group
- Повторная компиляция шаблона в цикле, что ведет к ухудшению производительности. Предпочтительнее закомпилировать
Patternодин раз и переиспользоватьMatcher. - Неправильное использование флагов:
String.matchesне принимает флаги; для флагов требуетсяPattern.compile(..., flags).
Изменения и развитие поддержки
За последние релизы Java не было радикальных изменений в поведении метода matches как такового. Изменения касаются двигателя регулярных выражений в целом:
- Периодические обновления поддержки стандартов Unicode в Java приводят к изменениям в поведенческих характеристиках классов
PatternиMatcherпри работе с символьными классами. - Добавление и улучшение синтаксиса регулярных выражений происходило раньше (например, именованные группы доступны в современных версиях). Важно проверять совместимость конкретных флагов и расширений с используемой версией JVM.
- Оптимизации производительности движка regex проводятся постепенно, однако API сохраняет обратную совместимость.
Расширенные примеры и подходы
Подробные варианты применения с пояснениями.
// 1. Валидация email (простая проверка)
String email = "user.name+tag@example.co.uk";
String emailRegex = "(?i)^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$"; // (?i) - флаг игнорирования регистра
System.out.println(email.matches(emailRegex));
true
Пояснение: для более строгой валидации требуется более сложный шаблон; данный пример показывает использование встроенного модификатора (?i).
// 2. Использование именованных групп и извлечение данных
java.util.regex.Pattern p = java.util.regex.Pattern.compile("(?\\d{3})-(?\\d{4})");
java.util.regex.Matcher m = p.matcher("123-4567");
if (m.matches()) {
System.out.println(m.group("area"));
System.out.println(m.group("local"));
}
123 4567
Пояснение: именованные группы удобны для читаемости и получения данных по имени вместо номера группы.
// 3. Повторное использование Pattern для производительности
java.util.regex.Pattern datePattern = java.util.regex.Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
String[] values = {"2020-01-01", "not a date", "1999-12-31"};
for (String v : values) {
System.out.println(v + " -> " + datePattern.matcher(v).matches());
}
2020-01-01 -> true not a date -> false 1999-12-31 -> true
// 4. Многострочный и DOTALL режимы
String text = "first line\nsecond line";
// (?s) включает DOTALL: точка совпадает с символом перевода строки
System.out.println(text.matches("(?s).*second.*"));
// (?m) включает MULTILINE: ^ и $ работают по-строчно
System.out.println(text.matches("(?m)^second.*$"));
true false
Пояснение: первый пример ищет подстроку, включая перевод строки. Второй пытается сопоставить всю строку с шаблоном, начинающимся с "second", поэтому возвращается false.
// 5. Использование lookahead для условий
String s = "abc123";
System.out.println(s.matches("(?=.*\\d)(?=.*[a-zA-Z]).+")); // проверка, что есть и буквы, и цифры
true
Пояснение: положительные lookahead позволяют описать комплексные условия без захвата частей строки.
// 6. Замены с обратными ссылками
String date = "2020/12/31";
System.out.println(date.replaceAll("(\\d{4})/(\\d{2})/(\\d{2})", "$3.$2.$1"));
31.12.2020
Пояснение: после проверки соответствия часто следует прямая замена с использованием групп.
// 7. Предотвращение ReDoS: использование жадных и ленивых квантификаторов аккуратно
String evil = "aaaaaaaaaaaaaaaaaaaaaaaa!";
// опасный шаблон, приводящий к долгой обработке при определённых входных данных
String dangerous = "^(a+)+!$";
System.out.println(evil.matches(dangerous)); // может быть медленно
// безопаснее: использовать ограничение на длину или альтернативы
String safer = "^(?:a{1,100})+!$";
System.out.println(evil.matches(safer));
false false
Пояснение: при проектировании выражений важно учитывать потенциальные атаки на регулярные выражения и ограничивать ввод или структуру шаблонов.