java.util.regex - pentingnya Pattern.compile ()?


118

Apa pentingnya Pattern.compile()metode?
Mengapa saya perlu mengkompilasi string regex sebelum mendapatkan Matcherobjek?

Sebagai contoh :

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

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

2
Nah, pentingnya hampir TIDAK ADA jika implementasi (seperti di JDK 1.7) hanya SHORTCUT belaka ke Pola baru (regex, 0); Meskipun demikian, kepentingan REAL bukanlah metode statis itu sendiri, tetapi pembuatan dan pengembalian Pola baru yang dapat disimpan untuk penggunaan selanjutnya. Mungkin ada implementasi lain di mana metode statis mengambil rute baru dan menyimpan objek Pattern, dan itu akan menjadi kasus nyata pentingnya Pattern.compile ()!
marcolopes

Jawabannya menyoroti pentingnya memisahkan pola dan kelas yang cocok (yang mungkin ditanyakan oleh pertanyaan), tetapi tidak ada yang menjawab mengapa kita tidak bisa menggunakan konstruktor new Pattern(regex)daripada fungsi kompilasi statis. komentar marcolopes ada di tempat.
kon psych

Jawaban:


144

The compile()Metode selalu disebut di beberapa titik; itu satu-satunya cara untuk membuat objek Pola. Jadi pertanyaannya adalah, mengapa Anda harus menyebutnya secara eksplisit ? Salah satu alasannya adalah Anda memerlukan referensi ke objek Matcher sehingga Anda dapat menggunakan metodenya, seperti group(int)mengambil konten dari grup penangkap. Satu-satunya cara untuk menguasai objek Matcher adalah melalui metode objek Pattern matcher(), dan satu-satunya cara untuk menguasai objek Pattern adalah melalui compile()metode. Lalu ada find()metode yang, tidak seperti matches(), tidak diduplikasi di kelas String atau Pola.

Alasan lainnya adalah untuk menghindari membuat objek Pattern yang sama berulang kali. Setiap kali Anda menggunakan salah satu metode bertenaga regex dalam String (atau matches()metode statis dalam Pola), itu membuat Pola baru dan Pencocokan baru. Jadi cuplikan kode ini:

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

... persis sama dengan ini:

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

Jelas, itu melakukan banyak pekerjaan yang tidak perlu. Faktanya, perlu waktu lebih lama untuk mengompilasi regex dan membuat instance objek Pattern dengan mudah, daripada melakukan pencocokan sebenarnya. Jadi biasanya masuk akal untuk menarik langkah itu keluar dari lingkaran. Anda juga dapat membuat Matcher sebelumnya, meskipun harganya tidak terlalu mahal:

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

Jika Anda terbiasa dengan regex .NET, Anda mungkin bertanya-tanya apakah compile()metode Java terkait dengan RegexOptions.Compiledpengubah .NET ; jawabannya adalah tidak. Pattern.compile()Metode Java hanya setara dengan konstruktor Regex .NET. Saat Anda menentukan Compiledopsi:

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

... itu mengkompilasi regex langsung ke kode byte CIL, memungkinkannya untuk bekerja lebih cepat, tetapi dengan biaya yang signifikan dalam pemrosesan di muka dan penggunaan memori - anggap saja sebagai steroid untuk regex. Java tidak memiliki padanan; tidak ada perbedaan antara Pola yang dibuat di belakang layar oleh String#matches(String)dan yang Anda buat secara eksplisit Pattern#compile(String).

(EDIT: Awalnya saya mengatakan bahwa semua objek .NET Regex di-cache, yang tidak benar. Sejak .NET 2.0, cache otomatis hanya terjadi dengan metode statis seperti Regex.Matches(), bukan ketika Anda memanggil konstruktor Regex secara langsung. Ref )


1
Namun, ini tidak menjelaskan pentingnya metode TRIVIAL pada kelas Pola! Saya selalu berasumsi bahwa metode statis Pattern.compile lebih dari sekedar SHORTCUT sederhana ke Pola baru (regex, 0); Saya mengharapkan CACHE pola terkompilasi ... saya salah. Mungkin membuat cache lebih mahal daripada membuat pola baru ??!
marcolopes

9
Harap dicatat bahwa kelas Matcher bukanlah thread safe dan tidak boleh dibagikan di seluruh thread. Di sisi lain Pattern.compile () adalah.
gswierczynski

1
TLDR; "... [Pattern.compile (...)] mengkompilasi regex secara langsung ke kode byte CIL, yang memungkinkannya bekerja lebih cepat, tetapi dengan biaya yang signifikan dalam pemrosesan di muka dan penggunaan memori"
sean.boyer

3
Meskipun benar bahwa Matcher tidak semahal Pattern.compile, saya melakukan beberapa metrik dalam skenario di mana ribuan pencocokan regex terjadi dan ada penghematan tambahan yang sangat signifikan dengan membuat Matcher sebelumnya dan menggunakannya kembali melalui matcher .reset (). Menghindari pembuatan objek baru di heap dalam metode yang dipanggil ribuan kali biasanya jauh lebih ringan pada CPU, memori, dan GC.
Volksman

@Volksman itu bukan saran umum yang aman karena objek Matcher tidak aman untuk thread. Itu juga tidak relevan dengan pertanyaan itu. Tapi ya, Anda bisa resetmenggunakan objek Matcher yang hanya digunakan oleh satu utas dalam satu waktu untuk mengurangi alokasi.
AndrewF

40

Compile mengurai ekspresi reguler dan membuat representasi dalam memori . Overhead untuk dikompilasi signifikan dibandingkan dengan pertandingan. Jika Anda menggunakan pola berulang kali , akan ada beberapa performa untuk menyimpan pola yang dikompilasi ke dalam cache.


7
Selain itu, Anda dapat menentukan flag seperti case_insensitive, dot_all, dll. Selama kompilasi, dengan meneruskan parameter flag tambahan
Sam Barnum

17

Ketika Anda mengkompilasi PatternJava melakukan beberapa perhitungan untuk Stringmempercepat pencarian kecocokan . (Membangun representasi regex dalam memori)

Jika Anda akan menggunakan kembali Patternberkali-kali, Anda akan melihat peningkatan kinerja yang pesat dibandingkan membuat yang baru Patternsetiap saat.

Dalam kasus hanya menggunakan Pola sekali, langkah kompilasi hanya tampak seperti baris kode tambahan, tetapi, pada kenyataannya, ini bisa sangat membantu dalam kasus umum.


5
Tentu Anda bisa menulis semuanya dalam satu baris Matcher matched = Pattern.compile(regex).matcher(text);. Ada keuntungan dari hal ini dibandingkan dengan memperkenalkan satu metode: argumen diberi nama secara efektif dan jelas bagaimana memfaktorkan keluar Patternuntuk kinerja yang lebih baik (atau untuk membagi metode).
Tom Hawtin - tackline

1
Sepertinya Anda selalu tahu banyak tentang Java. Mereka harus mempekerjakan Anda untuk bekerja untuk mereka ...
jjnguy

5

Ini adalah masalah kinerja dan penggunaan memori, kompilasi dan pertahankan pola yang sesuai jika Anda perlu sering menggunakannya. Penggunaan regex yang khas adalah untuk memvalidasi input pengguna (format) , dan juga memformat data keluaran untuk pengguna , di kelas-kelas ini, menyimpan pola yang sesuai, tampaknya cukup logis karena biasanya disebut banyak.

Di bawah ini adalah contoh validator, yang disebut banyak :)

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;
    }    
}

Seperti yang disebutkan oleh @Alan Moore, jika Anda memiliki regex yang dapat digunakan kembali dalam kode Anda, (sebelum loop misalnya), Anda harus mengkompilasi dan menyimpan pola untuk digunakan kembali.


2

Pattern.compile()memungkinkan untuk menggunakan regex beberapa kali (ini aman untuk thread). Manfaat kinerja bisa sangat signifikan.

Saya melakukan patokan cepat:

    @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 berada di antara 3x dan 4x lebih cepat . Saya rasa ini sangat tergantung pada regex itu sendiri, tetapi untuk regex yang sering digunakan, saya memilihstatic Pattern pattern = Pattern.compile(...)


0

Mengompilasi regex sebelumnya akan meningkatkan kecepatan. Menggunakan kembali Matcher memberi Anda sedikit percepatan. Jika metode dipanggil sering mengatakan dipanggil dalam satu lingkaran, kinerja keseluruhan pasti akan naik.


0

Mirip dengan 'Pattern.compile' ada 'RECompiler.compile' [dari com.sun.org.apache.regexp.internal] di mana:
1. kode yang dikompilasi untuk pola [az] memiliki 'az' di dalamnya
2. kode yang dikompilasi untuk pola [0-9] memiliki '09' di dalamnya
3. kode kompilasi untuk pola [abc] memiliki 'aabbcc' di dalamnya.

Jadi kode yang dikompilasi adalah cara terbaik untuk menggeneralisasi banyak kasus. Jadi alih-alih memiliki situasi penanganan kode yang berbeda 1,2 dan 3. Masalahnya berkurang menjadi membandingkan dengan ascii elemen sekarang dan berikutnya dalam kode yang dikompilasi, karenanya berpasangan. Jadi
a. apapun dengan ascii antara a dan z adalah antara a dan z
b. apapun dengan ascii antara 'a dan a pasti' a '


0

Kelas pola adalah titik masuk mesin regex. Anda dapat menggunakannya melalui Pattern.matches () dan Pattern.comiple (). #Perbedaan antara keduanya. match () - untuk memeriksa dengan cepat apakah teks (String) cocok dengan ekspresi reguler comiple () - buat referensi Pola. Jadi bisa menggunakan beberapa kali untuk mencocokkan ekspresi reguler dengan beberapa teks.

Sebagai referensi:

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()+ " ");
     }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.