java.util.regex - Pattern.compile () öğesinin önemi?


118

Pattern.compile()Yöntemin önemi nedir ? Nesneyi
almadan önce neden normal ifade dizesini derlemem gerekiyor Matcher?

Örneğin :

String regex = "((\\S+)\\s*some\\s*";

Pattern pattern = Pattern.compile(regex); // why do I need to compile
Matcher matcher = pattern.matcher(text);

2
Pekala, eğer uygulama (JDK 1.7'deki gibi) yeni Kalıp için sadece KISAYOL ise (regex, 0) önem neredeyse HİÇBİRDİR; Bununla birlikte, GERÇEK önem statik yöntemin kendisi değil, ikinci kullanım için kaydedilebilecek yeni bir Kalıp yaratılması ve geri dönmesidir. Statik yöntemin yeni bir rota aldığı ve Pattern nesnelerini önbelleğe aldığı başka uygulamalar da olabilir ve bu gerçek bir Pattern.compile () durumu olabilir!
marcolopes

Cevaplar örüntüyü ayırmanın ve sınıfları eşleştirmenin önemini vurguluyor (muhtemelen sorunun sorduğu budur), ancak kimse neden new Pattern(regex)statik bir derleme işlevi yerine bir kurucu kullanamıyoruz yanıtını vermiyor . marcolopes yorumu yerinde.
kon psych

Yanıtlar:


144

compile()Yöntem her zaman bir noktada denir; bir Pattern nesnesi oluşturmanın tek yolu budur. Öyleyse soru gerçekten, neden açıkça adlandırmalısınız ? Bunun nedenlerinden biri group(int), yakalama gruplarının içeriğini almak gibi yöntemlerini kullanabilmeniz için Matcher nesnesine bir referansa ihtiyacınız olmasıdır . Matcher nesnesine ulaşmanın tek yolu, Pattern nesnesinin matcher()yöntemini kullanmaktır ve Pattern nesnesine ulaşmanın tek yolu compile()yöntemdir. Ardından , String veya Pattern sınıflarında find()aksine matches(), yinelenmeyen bir yöntem var .

Diğer neden, aynı Pattern nesnesini tekrar tekrar oluşturmaktan kaçınmaktır. String'de regex destekli yöntemlerden birini (veya matches()Pattern'deki statik yöntemi) her kullandığınızda, yeni bir Desen ve yeni bir Eşleştirici oluşturur. Yani bu kod pasajı:

for (String s : myStringList) {
    if ( s.matches("\\d+") ) {
        doSomething();
    }
}

... tam olarak buna eşdeğerdir:

for (String s : myStringList) {
    if ( Pattern.compile("\\d+").matcher(s).matches() ) {
        doSomething();
    }
}

Açıkçası, bu çok fazla gereksiz iş yapıyor. Aslında, regex'i derlemek ve Pattern nesnesini somutlaştırmak, gerçek bir eşleştirme yapmaktan kolayca daha uzun sürebilir. Bu nedenle, genellikle bu adımı döngüden çıkarmak mantıklıdır. Eşleştiriciyi, neredeyse o kadar pahalı olmasalar da, önceden de oluşturabilirsiniz:

Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("");
for (String s : myStringList) {
    if ( m.reset(s).matches() ) {
        doSomething();
    }
}

.NET regexlerine aşina iseniz, Java compile()yönteminin .NET'in RegexOptions.Compileddeğiştiricisiyle ilişkili olup olmadığını merak ediyor olabilirsiniz ; cevap hayır. Java'nın Pattern.compile()yöntemi yalnızca .NET'in Regex yapıcısına eşdeğerdir. CompiledSeçeneği belirttiğinizde :

Regex r = new Regex(@"\d+", RegexOptions.Compiled); 

... regex'i doğrudan CIL bayt koduna derler ve çok daha hızlı çalışmasına izin verir, ancak ön işlem ve bellek kullanımında önemli bir maliyetle - bunu regexler için steroidler olarak düşünün. Java'nın eşdeğeri yoktur; Perde arkasında yaratılan bir Kalıp String#matches(String)ile açıkça oluşturduğunuz bir Kalıp arasında hiçbir fark yoktur Pattern#compile(String).

(DÜZENLEME: Başlangıçta tüm .NET Regex nesnelerinin önbelleğe alındığını söyledim, bu yanlış. .NET 2.0'dan beri, otomatik önbelleğe alma, Regex.Matches()doğrudan bir Regex yapıcısını çağırdığınızda değil, yalnızca statik yöntemlerle gerçekleşir . Ref )


1
Yine de bu, Pattern sınıfında böyle bir TRIVIAL metodunun önemini açıklamıyor! Ben hep Pattern.compile statik metodunun yeni Pattern (regex, 0) için basit bir SHORTCUT'dan çok daha fazlası olduğunu varsaydım; Derlenmiş modellerden oluşan bir CACHE bekliyordum ... yanılmışım. Belki bir önbellek oluşturmak, yeni desenler oluşturmaktan daha pahalıdır ??!
marcolopes

9
Matcher sınıfının iş parçacığı için güvenli olmadığını ve iş parçacıkları arasında paylaşılmaması gerektiğini lütfen unutmayın. Öte yandan Pattern.compile ().
gswierczynski

1
TLDR; "... [Pattern.compile (...)], normal ifadeyi doğrudan CIL bayt kodunda derler, çok daha hızlı performans göstermesine izin verir, ancak ön işlem ve bellek kullanımında önemli bir maliyetle"
sean.boyer

3
Eşleştiricilerin Pattern.compile kadar pahalı olmadığı doğru olsa da, binlerce regex eşleşmesinin gerçekleştiği bir senaryoda bazı ölçümler yaptım ve Matcher'ı önceden oluşturup eşleştirici aracılığıyla yeniden kullanarak ek, çok önemli bir tasarruf sağladım .Sıfırla(). Binlerce kez denilen yöntemlerde yığın içinde yeni nesnelerin oluşturulmasından kaçınmak genellikle CPU, bellek ve dolayısıyla GC'de çok daha hafiftir.
Volksman

@Volksman güvenli olmayan genel tavsiye çünkü Matcher nesneleri iş parçacığı güvenli değildir. Soruyla da alakalı değil. Ama evet, resettahsisleri azaltmak için bir seferde yalnızca bir iş parçacığı tarafından kullanılan bir Matcher nesnesi yapabilirsiniz .
AndrewF

40

Compile , normal ifadeyi ayrıştırır ve bir bellek içi gösterim oluşturur . Derlenecek ek yük, bir eşleşmeye kıyasla önemlidir. Bir kalıbı tekrar tekrar kullanıyorsanız , derlenen kalıbı önbelleğe almak biraz performans kazanacaktır.


7
Ayrıca, derleme sırasında fazladan bir bayrak parametresi ileterek case_insensitive, dot_all, vb. Bayrakları belirtebilirsiniz
Sam Barnum

17

PatternJava'yı derlediğinizde, eşlemeleri Stringdaha hızlı bulmak için bazı hesaplamalar yapar . (Normal ifadenin bellek içi temsilini oluşturur)

Birden Patternçok kez yeniden kullanacaksanız, Patternher seferinde yeni bir tane oluşturmaya göre büyük bir performans artışı göreceksiniz .

Kalıp'ı yalnızca bir kez kullanmak durumunda, derleme adımı fazladan bir kod satırı gibi görünür, ancak aslında genel durumda çok yardımcı olabilir.


5
Elbette hepsini tek satırda yazabilirsiniz Matcher matched = Pattern.compile(regex).matcher(text);. Bunun, tek bir yöntemi uygulamaya kıyasla avantajları vardır: argümanlar etkin bir şekilde adlandırılmıştır ve Patterndaha iyi performans için (veya yöntemler arasında bölünme) nasıl hesaba katılacağı açıktır .
Tom Hawtin - tackline

1
Java hakkında her zaman çok şey biliyor gibisin. Onlar için çalışmak için sizi işe
almalılar

5

Önemli olan performans ve bellek kullanımıdır, çok fazla kullanmanız gerekiyorsa uygun modeli derleyin ve saklayın. Düzenli ifadenin tipik bir kullanımı, doğrulanmış kullanıcı girdisi (biçim) ve ayrıca , bu sınıflarda kullanıcılar için çıktı verilerini biçimlendirmektir , uyumlu örüntüyü kaydetmek, genellikle çok dedikleri için oldukça mantıklı görünür.

Aşağıda, gerçekten çok adlandırılan örnek bir doğrulayıcı var :)

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 tarafından belirtildiği gibi, kodunuzda yeniden kullanılabilir regex varsa (örneğin bir döngüden önce), yeniden kullanım için deseni derlemeniz ve kaydetmeniz gerekir.


2

Pattern.compile()bir normal ifadenin birden çok kez yeniden kullanılmasına izin verin (iş parçacığı güvenlidir). Performans avantajı oldukça önemli olabilir.

Hızlı bir kıyaslama yaptım:

    @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 ile 4x arasında daha hızlıydı . Sanırım bu büyük ölçüde normal ifadenin kendisine bağlıdır, ancak sıklıkla kullanılan bir normal ifade içinstatic Pattern pattern = Pattern.compile(...)


0

Düzenli ifadeyi önceden derlemek hızı artırır. Eşleştiriciyi yeniden kullanmak size biraz daha hız kazandırır. Yöntem sık sık çağrılırsa, bir döngü içinde çağrılırsa, genel performans kesinlikle artacaktır.


0

'Pattern.compile'a benzer şekilde' RECompiler.compile 'vardır [com.sun.org.apache.regexp.internal'dan] burada:
1. [az] kalıbı için derlenmiş kodda' az 'vardır
2. için derlenmiş kod [0-9] kalıbı içinde '09' var
3. [abc] kalıbı için derlenmiş kod içinde 'aabbcc' var.

Bu nedenle derlenmiş kod, birden çok durumu genelleştirmek için harika bir yoldur. Böylece, farklı kod işleme durumu 1,2 ve 3'e sahip olmak yerine. Sorun, derlenen koddaki mevcut ve sonraki öğenin ascii ile karşılaştırılmasına, dolayısıyla çiftlere indirgenir. Böylece
a. a ve z arasında ascii olan her şey a ile z
b arasındadır . ascii 'a ve a arasında olan herhangi bir şey kesinlikle' a'dır


0

Pattern sınıfı, normal ifade motorunun giriş noktasıdır. Pattern.matches () ve Pattern.comiple () aracılığıyla kullanabilirsiniz. # Bu ikisi arasındaki fark. eşleşmeler () - bir metnin (Dize) belirli bir düzenli ifade comiple () ile eşleşip eşleşmediğini hızlıca kontrol etmek için - Pattern başvuru oluşturun. Böylece, normal ifadeyi birden çok metinle eşleştirmek için birden çok kez kullanılabilir.

Referans için:

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()+ " ");
     }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.