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(...)