java.util.regex - Pattern.compile () का महत्व?


118

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

2
यदि कार्यान्वयन (JDK 1.7 की तरह) केवल नए पैटर्न (regex, 0) के लिए उचित है, तो इसका महत्व लगभग नहीं है; उस ने कहा, वास्तविक महत्व स्वयं स्थिर विधि नहीं है, बल्कि एक नए पैटर्न का निर्माण और वापसी है जिसे बाद के उपयोग के लिए बचाया जा सकता है। हो सकता है कि अन्य कार्यान्वयन भी हों जहाँ स्थैतिक विधि एक नया मार्ग लेती है और पैटर्न ऑब्जेक्ट्स को कैश करती है, और यह पैटर्न.कॉमपाइल () महत्व का वास्तविक मामला होगा!
मार्कोलोप्स

उत्तर पैटर्न और मिलान वर्गों को अलग करने के महत्व पर प्रकाश डालते हैं (जो कि शायद यही सवाल पूछते हैं), लेकिन कोई भी जवाब नहीं देता कि हम new Pattern(regex)स्थैतिक संकलन फ़ंक्शन के बजाय केवल एक कंस्ट्रक्टर का उपयोग क्यों नहीं कर सकते । marcolopes टिप्पणी मौके पर है।
कोन मन

जवाबों:


144

compile()विधि हमेशा कुछ बिंदु पर कहा जाता है; यह एक पैटर्न ऑब्जेक्ट बनाने का एकमात्र तरीका है। तो सवाल वास्तव में है, आपको इसे स्पष्ट रूप से क्यों कहना चाहिए ? एक कारण यह है कि आपको मिलानकर्ता ऑब्जेक्ट के संदर्भ की आवश्यकता है ताकि आप इसके तरीकों का उपयोग कर सकें, जैसे group(int)कि कैप्चरिंग समूहों की सामग्री को पुनः प्राप्त करना। माचिस की वस्तु के छिद्र को प्राप्त करने का एकमात्र तरीका पैटर्न ऑब्जेक्ट की matcher()विधि के माध्यम से है , और पैटर्न के ऑब्जेक्ट को देखने का एकमात्र तरीका compile()विधि के माध्यम से है। फिर वहाँ find()विधि है, जो इसके विपरीत matches(), स्ट्रिंग या पैटर्न वर्गों में डुप्लिकेट नहीं है।

दूसरा कारण एक ही पैटर्न ऑब्जेक्ट को बार-बार बनाने से बचना है। जब भी आप स्ट्रिंग में (या matches()पैटर्न में स्थिर विधि) रीगेक्स-संचालित विधियों में से एक का उपयोग करते हैं , तो यह एक नया पैटर्न और एक नया मिलान बनाता है। तो यह कोड स्निपेट:

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

... इस के बराबर है:

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

जाहिर है, यह अनावश्यक काम कर रहा है। वास्तव में, यह आसानी से रेगेक्स को संकलित करने और पैटर्न ऑब्जेक्ट को इंस्टेंट करने में अधिक समय ले सकता है, क्योंकि यह वास्तविक मैच करने के लिए करता है। तो यह आम तौर पर लूप से उस कदम को खींचने के लिए समझ में आता है। आप मिलानकर्ता को समय से पहले बना सकते हैं, हालांकि वे लगभग इतने महंगे नहीं हैं:

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

यदि आप .NET रेगेक्स से परिचित हैं, तो आप सोच रहे होंगे कि क्या जावा की compile()विधि .NET के RegexOptions.Compiledसंशोधक से संबंधित है ; जवाब न है। जावा का Pattern.compile()तरीका .NET के रेगेक्स कंस्ट्रक्टर के समतुल्य है। जब आप Compiledविकल्प निर्दिष्ट करें :

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

... यह regex को CIL बाइट कोड से सीधे संकलित करता है, जिससे यह बहुत तेज़ी से प्रदर्शन कर सकता है, लेकिन अप-फ्रंट प्रोसेसिंग और मेमोरी उपयोग में एक महत्वपूर्ण लागत पर - इसे regexes के लिए स्टेरॉयड के रूप में सोचें। जावा का कोई समकक्ष नहीं है; उस पैटर्न के बीच कोई अंतर नहीं है जो String#matches(String)आपके द्वारा बनाए गए दृश्यों के पीछे और आपके द्वारा स्पष्ट रूप से बनाया गया है Pattern#compile(String)

(संपादित करें: मैं मूल रूप से कहा गया है कि सभी .NET Regex वस्तुओं कैश नहीं किया जाता है, जो सही नहीं है .NET 2.0 के बाद से, स्वचालित संचयन होता है स्थिर तरीकों की तरह ही है। Regex.Matches(), जब नहीं आप सीधे एक Regex निर्माता कहते हैं। रेफरी )


1
फिर भी, यह पैटर्न वर्ग पर इस तरह के एक महत्वपूर्ण पद्धति का महत्व नहीं समझाता है! मैंने हमेशा यह माना कि स्थिर पद्धति Pattern.compile एक साधारण SHORTCUT से नए पैटर्न (regex, 0) की तुलना में बहुत अधिक थी; मैं संकलित पैटर्न के एक CACHE उम्मीद कर रहा था ... मैं गलत था। हो सकता है कि नए पैटर्न बनाने से ज्यादा महंगा कैश हो ??!
23-02 को मार्च

9
कृपया ध्यान दें कि माचिस क्लास थ्रेड सुरक्षित नहीं है और इसे थ्रेड्स में साझा नहीं किया जाना चाहिए। दूसरी ओर Pattern.compile () है।
gswierczynski

1
TLDR; "... [Pattern.compile (...)] रेगेक्स को CIL बाइट कोड से सीधे संकलित करता है, जिससे यह बहुत तेजी से प्रदर्शन कर सकता है, लेकिन अप-फ्रंट प्रोसेसिंग और मेमोरी उपयोग में एक महत्वपूर्ण लागत पर"
sean.boyer

3
हालांकि यह सच है कि मैचर्स लगभग उतना महंगा नहीं है जितना कि पैटर्न.कॉमपाइल मैंने कुछ मेट्रिक्स को एक परिदृश्य में किया, जहां हजारों रेगेक्स मैच हो रहे थे और मैचर को समय से पहले बनाकर और मैचर के माध्यम से पुन: उपयोग करके एक अतिरिक्त, बहुत महत्वपूर्ण बचत थी ।रीसेट()। हज़ारों बार तरीकों से ढेर में नई वस्तुओं के निर्माण से बचना आमतौर पर सीपीयू, मेमोरी और इस प्रकार जीसी पर बहुत हल्का होता है।
वोक्समैन

@ वोल्समैन जो सुरक्षित सामान्य सलाह नहीं है क्योंकि माचिस की वस्तुएं थ्रेडसेफ़ नहीं हैं। यह सवाल के लिए भी प्रासंगिक नहीं है। लेकिन हाँ, आप resetएक माचिस की वस्तु का उपयोग कर सकते हैं जो केवल आवंटन को कम करने के लिए एक समय में एक थ्रेड द्वारा उपयोग किया जाता है।
एंड्रयूज

40

संकलन नियमित अभिव्यक्ति को पार्स करता है और इन-मेमोरी प्रतिनिधित्व बनाता है । एक मैच की तुलना में संकलन करने के लिए ओवरहेड महत्वपूर्ण है। यदि आप बार-बार एक पैटर्न का उपयोग कर रहे हैं तो यह संकलित पैटर्न को कैश करने के लिए कुछ प्रदर्शन प्राप्त करेगा।


7
इसके अलावा, आप अतिरिक्त झंडे पैरामीटर में पास करके, संकलित करने के दौरान Case_insensitive, dot_all आदि जैसे झंडे निर्दिष्ट कर सकते हैं
सैम बरनम

17

जब आप संकलित करते हैं तो Patternजावा Stringतेजी से मैच खोजने के लिए कुछ गणना करता है । (रेगेक्स का इन-मेमोरी प्रतिनिधित्व बनाता है)

यदि आप Patternकई बार पुन: उपयोग करने जा रहे हैं, तो आपको Patternहर बार एक नया बनाने पर एक विशाल प्रदर्शन वृद्धि दिखाई देगी ।

केवल एक बार पैटर्न का उपयोग करने के मामले में, संकलन कदम बस कोड की एक अतिरिक्त रेखा की तरह लगता है, लेकिन वास्तव में, यह सामान्य मामले में बहुत मददगार हो सकता है।


5
बेशक आप इसे एक पंक्ति में लिख सकते हैं Matcher matched = Pattern.compile(regex).matcher(text);। एक एकल विधि को शुरू करने के लिए इसके फायदे हैं: तर्कों को प्रभावी रूप से नाम दिया गया है और यह स्पष्ट है कि Patternबेहतर प्रदर्शन के लिए कैसे कारक है (या तरीकों में विभाजित करने के लिए)।
टॉम हॉन्टिन -

1
यह हमेशा ऐसा लगता है जैसे आप जावा के बारे में इतना जानते हैं। उन्हें आपको उनके लिए काम करने के लिए काम पर रखना चाहिए ...
jjnguy

5

यह प्रदर्शन और स्मृति के उपयोग की बात है, यदि आपको इसे बहुत उपयोग करने की आवश्यकता है, तो संकलित पैटर्न को संकलित करें और रखें। रेगेक्स का एक विशिष्ट उपयोग उपयोगकर्ता इनपुट (प्रारूप) को मान्य करने के लिए है , और उपयोगकर्ताओं के लिए आउटपुट डेटा को भी प्रारूपित करता है , इन वर्गों में, अनुपालन पैटर्न को सहेजते हुए, काफी तार्किक लगता है क्योंकि वे आमतौर पर बहुत कुछ कहते हैं।

नीचे एक नमूना सत्यापनकर्ता है, जिसे वास्तव में बहुत कुछ कहा जाता है :)

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 मूर ने उल्लेख किया है, यदि आपके पास अपने कोड में पुन: प्रयोज्य rexx है, (उदाहरण के लिए लूप से पहले), तो आपको पुन: उपयोग के लिए पैटर्न संकलित करना और सहेजना होगा।


2

Pattern.compile()एक regex को कई बार पुन: उपयोग करने की अनुमति दें (यह थ्रेडसेफ़ है)। प्रदर्शन लाभ काफी महत्वपूर्ण हो सकता है।

मैंने एक त्वरित बेंचमार्क किया:

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


0

रेगेक्स को प्री-कंपाइल करने से स्पीड बढ़ जाती है। माचिस का फिर से उपयोग करने से आपको एक और मामूली स्पीडअप मिलता है। यदि विधि को अक्सर कहा जाता है कि लूप के भीतर बुलाया जाता है, तो समग्र प्रदर्शन निश्चित रूप से ऊपर जाएगा।


0

'Pattern.compile' के समान ही 'RECompiler.compile' है [com.sun.org.apache.regexp.internal] से: जहाँ
पैटर्न के लिए 1. संकलित कोड [az] में 'az' है:
2. के लिए संकलित कोड पैटर्न [0-9] में '09' है
। पैटर्न के लिए संकलित कोड [abc] में 'aabbcc' है।

इस प्रकार संकलित कोड कई मामलों को सामान्य बनाने का एक शानदार तरीका है। इस प्रकार अलग कोड हैंडलिंग स्थिति 1,2 और 3 होने के बजाय। संकलित कोड में वर्तमान और अगले तत्व की एससीआई के साथ तुलना करने के लिए समस्या कम हो जाती है, इसलिए जोड़े। इस प्रकार
ए। एक और z के बीच ascii कुछ भी एक और z
b के बीच है । 'a' और 'a' के बीच कुछ भी नहीं है


0

पैटर्न वर्ग रेगेक्स इंजन का प्रवेश बिंदु है। आप इसका उपयोग पैटर्न.matches () और Pattern.comiple () के माध्यम से कर सकते हैं। इन दोनों के बीच #Difference। माचिस () - जल्दी से जांच के लिए कि क्या कोई पाठ (स्ट्रिंग) किसी दिए गए रेगुलर एक्सप्रेशन कॉम्पटिशन () से मेल खाता है - पैटर्न का संदर्भ बनाएं। इसलिए कई ग्रंथों के खिलाफ नियमित अभिव्यक्ति का मिलान करने के लिए कई बार उपयोग कर सकते हैं।

सन्दर्भ के लिए:

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()+ " ");
     }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.