Java.util.regex - важность Pattern.compile()?
Какова важность метода Pattern.compile()
?
Зачем мне компилировать строку регулярного выражения до получения объекта Matcher
?
Например:
String regex = "((\\S+)\\s*some\\s*";
Pattern pattern = Pattern.compile(regex); // why do I need to compile
Matcher matcher = pattern.matcher(text);
Ответы
Ответ 1
Метод compile()
всегда вызывается в какой-то момент; это единственный способ создать объект Pattern. Итак, вопрос в том, почему вы должны называть его явно? Одна из причин заключается в том, что вам нужна ссылка на объект Matcher, поэтому вы можете использовать его методы, например group(int)
, для извлечения содержимого групп захвата. Единственный способ получить объект "Матчи" - через метод объекта "Образец" matcher()
, и единственный способ получить объект объекта "Шаблон" - через метод compile()
. Тогда существует метод find()
, который, в отличие от matches()
, не дублируется в классах String или Pattern.
Другая причина заключается в том, чтобы избежать повторного создания одного и того же объекта шаблона. Каждый раз, когда вы используете один из методов с регулярным выражением в String (или статический метод matches()
в шаблоне), он создает новый шаблон и новый Matcher. Итак, этот фрагмент кода:
for (String s : myStringList) {
if ( s.matches("\\d+") ) {
doSomething();
}
}
... точно эквивалентен этому:
for (String s : myStringList) {
if ( Pattern.compile("\\d+").matcher(s).matches() ) {
doSomething();
}
}
Очевидно, что это делает много ненужной работы. На самом деле, может легко потребоваться больше времени для компиляции регулярного выражения и создания экземпляра объекта Pattern, чем для фактического соответствия. Поэтому обычно имеет смысл вытащить этот шаг из цикла. Вы также можете создать Matcher, хотя они не так дороги:
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("");
for (String s : myStringList) {
if ( m.reset(s).matches() ) {
doSomething();
}
}
Если вы знакомы с регулярными выражениями .NET, вам может быть интересно, связан ли метод Java compile()
с .NET RegexOptions.Compiled
модификатором; ответ - нет. Метод Java Pattern.compile()
просто эквивалентен конструктору .NET Regex. Когда вы укажете опцию Compiled
:
Regex r = new Regex(@"\d+", RegexOptions.Compiled);
... он компилирует регулярное выражение непосредственно к байтовому коду CIL, что позволяет ему выполнять гораздо быстрее, но при значительных затратах на обработку перед использованием и использование памяти - считайте это стероидами для регулярных выражений. Java не имеет эквивалента; нет никакой разницы между шаблоном, созданным за кулисами String#matches(String)
, и тем, который вы создаете явно с помощью Pattern#compile(String)
.
(EDIT: Первоначально я сказал, что все объекты .NET Regex кэшируются, что неверно. С .NET 2.0 автоматическое кэширование происходит только со статическими методами, такими как Regex.Matches()
, а не при непосредственном вызове конструктора Regex. ref)
Ответ 2
Скомпилируйте анализирует регулярное выражение и строит представление в памяти. Накладные расходы для компиляции значительны по сравнению с совпадением. Если вы повторно используете шаблон , он получит некоторую производительность для кэширования скомпилированного шаблона.
Ответ 3
При компиляции Pattern
Java выполняет некоторые вычисления, чтобы быстрее находить совпадения в String
. (Создает представление в регулярном выражении в памяти)
Если вы собираетесь повторно использовать Pattern
несколько раз, вы увидите огромное увеличение производительности при создании нового Pattern
каждый раз.
В случае использования только одного шаблона шаг компиляции просто кажется дополнительной строкой кода, но на самом деле это может быть очень полезно в общем случае.
Ответ 4
Вопрос о производительности и использовании памяти, компиляции и сохранении шаблона, если вам нужно его использовать.
Типичное использование регулярного выражения - это проверенный пользовательский вход (формат), а также выходные данные формата для пользователей, в этих классах сохранение сохраненного шаблона кажется вполне логичным, поскольку они обычно называемый много.
Ниже приведен образец проверки, который действительно называется много:)
public class AmountValidator {
//Accept 123 - 123,456 - 123,345.34
private static final String AMOUNT_REGEX="\\d{1,3}(,\\d{3})*(\\.\\d{1,4})?|\\.\\d{1,4}";
//Compile and save the pattern
private static final Pattern AMOUNT_PATTERN = Pattern.compile(AMOUNT_REGEX);
public boolean validate(String amount){
if (!AMOUNT_PATTERN.matcher(amount).matches()) {
return false;
}
return true;
}
}
Как уже упоминалось @Alan Moore, если у вас есть многократно используемое регулярное выражение в вашем коде (перед циклом, например), вы должны скомпилировать и сохранить шаблон для повторного использования.
Ответ 5
Предварительная компиляция регулярного выражения увеличивает скорость. Повторное использование Матчи дает вам еще одно небольшое ускорение. Если метод получает вызов, часто говорят, что он вызван в цикле, общая производительность будет, безусловно, повышаться.
Ответ 6
Аналогично "Pattern.compile" есть "RECompiler.compile" [из com.sun.org.apache.regexp.internal], где:
1. скомпилированный код для pattern [a-z] содержит в себе 'az'
2. скомпилированный код для шаблона [0-9] содержит "09"
3. скомпилированный код для шаблона [abc] содержит aabbcc.
Таким образом, скомпилированный код - отличный способ обобщить несколько случаев. Таким образом, вместо того, чтобы иметь разные ситуации обработки кода 1,2 и 3. Проблема сводится к сравнению с ascii текущего и следующего элемента в скомпилированном коде, отсюда и пары.
таким образом
а. что-нибудь с ASCII между A и Z находится между A и Z
б. что-нибудь с ascii между 'a и a определенно' a '
Ответ 7
Класс Pattern является точкой входа движка регулярных выражений. Вы можете использовать его через Pattern.matches() и Pattern.comiple(). Разница между этими двумя.
match() - для быстрой проверки соответствия текста (String) заданному регулярному выражению
comiple() - создать ссылку на шаблон. Поэтому можно использовать несколько раз, чтобы сопоставить регулярное выражение с несколькими текстами.
Для справки:
public static void main(String[] args) {
//single time uses
String text="The Moon is far away from the Earth";
String pattern = ".*is.*";
boolean matches=Pattern.matches(pattern,text);
System.out.println("Matches::"+matches);
//multiple time uses
Pattern p= Pattern.compile("ab");
Matcher m=p.matcher("abaaaba");
while(m.find()) {
System.out.println(m.start()+ " ");
}
}
Ответ 8
Pattern.compile()
позволяет многократно использовать регулярные выражения (это потокобезопасно). Выигрыш в производительности может быть весьма значительным.
Я сделал быстрый тест:
@Test
public void recompile() {
var before = Instant.now();
for (int i = 0; i < 1_000_000; i++) {
Pattern.compile("ab").matcher("abcde").matches();
}
System.out.println("recompile " + Duration.between(before, Instant.now()));
}
@Test
public void compileOnce() {
var pattern = Pattern.compile("ab");
var before = Instant.now();
for (int i = 0; i < 1_000_000; i++) {
pattern.matcher("abcde").matches();
}
System.out.println("compile once " + Duration.between(before, Instant.now()));
}
compileOnce был между 3x и 4x быстрее.
Я предполагаю, что это сильно зависит от самого регулярного выражения, но для регулярного выражения, которое часто используется, я выбираю static Pattern pattern = Pattern.compile(...)