हम जावा रेगेक्स के साथ एक ^ nb ^ n मैच कैसे कर सकते हैं?


99

यह शैक्षिक रेगेक्स लेखों की एक श्रृंखला का दूसरा भाग है। यह दिखाता है कि कैसे lookaheads और नेस्टेड संदर्भ में गैर-नियमित भाषाओं का एक मैच के लिए इस्तेमाल किया जा सकता एन बी एन । नेस्टेड संदर्भ पहले में पेश किए गए हैं: यह रेगेक्स त्रिकोणीय संख्याओं को कैसे खोजता है?

एक चापलूसी गैर- नियमित भाषाओं में से एक है:

L = { an bn: n > 0 }

यह सभी गैर-रिक्त तारों की भाषा है, जिसमें कुछ संख्याओं के aबाद एक समान संख्या होती है b। इस भाषा में तार के उदाहरण हैं ab, aabb, aaabbb

पंपिंग लेम्मा द्वारा इस भाषा को गैर-नियमित दिखाया जा सकता है । यह वास्तव में एक कट्टरपंथी संदर्भ-मुक्त भाषा है , जिसे संदर्भ-मुक्त व्याकरण द्वारा उत्पन्न किया जा सकता है S → aSb | ab

बहरहाल, आधुनिक दिन रेगेक्स कार्यान्वयन स्पष्ट रूप से सिर्फ नियमित भाषाओं से अधिक पहचानते हैं। यही है, वे औपचारिक भाषा सिद्धांत परिभाषा द्वारा "नियमित" नहीं हैं। PCRE और पर्ल पुनरावर्ती rexx का समर्थन करता है, और .NET समूहों की परिभाषा का समर्थन करता है। इससे भी कम "फैंसी" सुविधाएँ, उदाहरण के लिए, मेल खाने वाले बैकरेन्शन का मतलब है कि रेगेक्स नियमित नहीं है।

लेकिन सिर्फ यह "बुनियादी" विशेषताएं कितनी शक्तिशाली हैं? क्या हम Lउदाहरण के लिए जावा रेगेक्स के साथ पहचान कर सकते हैं ? हम शायद lookarounds और नेस्टेड संदर्भ गठबंधन और एक पैटर्न है कि जैसे के साथ काम करता है हो सकता है String.matchesकी तरह तार मैच के लिए ab, aabb, aaabbb, आदि?

संदर्भ

जुड़े हुए प्रश्न


4
समुदाय में कुछ की अनुमति से यह श्रृंखला शुरू की गई थी ( meta.stackexchange.com/questions/62695/… )। यदि रिसेप्शन अच्छा है, तो मैं अन्य उन्नत और साथ ही रेगेक्स की अधिक बुनियादी विशेषताओं को कवर करना जारी रखना चाहता हूं।
पॉलीजेन लुब्रिकेंट्स


वाह, मुझे कभी नहीं पता था कि जावा के रेगेक्स नियमित अभिव्यक्तियों तक सीमित नहीं होंगे। मुझे लगता है कि बताते हैं कि मैंने हमेशा सोचा है कि वे पूरी तरह से लागू नहीं होंगे। मेरा मतलब है कि जावा रेक्सक्स में निर्मित कोई पूरक, अंतर या उत्पाद ऑपरेटर नहीं हैं, लेकिन इसका मतलब यह है कि वे नियमित भाषाओं तक सीमित नहीं हैं।
लैन

इस प्रश्न को "उन्नत रेगेक्स-फू" के तहत स्टैक ओवरफ्लो रेगुलर एक्सप्रेशन एफएक्यू में जोड़ा गया है ।
aliteralmind

जवाबों:


139

जवाब है, कहने की जरूरत नहीं है, हाँ! आप निश्चित रूप से एक n b n मैच के लिए जावा रेगेक्स पैटर्न लिख सकते हैं । यह दावे के लिए एक सकारात्मक रूप का उपयोग करता है, और "गिनती" के लिए एक नेस्टेड संदर्भ।

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

समाधान विकसित करने के लिए उपयोग की जाने वाली भाषा अपनी सुगमता के लिए PHP होगी। पैटर्न को अंतिम रूप देने के बाद अंतिम परीक्षा जावा में की जाएगी।


चरण 1: मुखरता के लिए लुकहेड

चलो एक सरल समस्या से शुरू करते हैं: हम a+एक स्ट्रिंग की शुरुआत में मेल खाना चाहते हैं , लेकिन केवल तभी जब इसका तुरंत पालन किया जाए b+। हम अपने मैच ^को एंकर करने के लिए उपयोग कर सकते हैं , और चूंकि हम केवल a+बिना मैच के चाहते हैं b+, हम लुकहेड जोर का उपयोग कर सकते हैं (?=…)

यहाँ एक सरल परीक्षण हार्नेस के साथ हमारा पैटर्न है:

function testAll($r, $tests) {
   foreach ($tests as $test) {
      $isMatch = preg_match($r, $test, $groups);
      $groupsJoined = join('|', $groups);
      print("$test $isMatch $groupsJoined\n");
   }
}
 
$tests = array('aaa', 'aaab', 'aaaxb', 'xaaab', 'b', 'abbb');
 
$r1 = '/^a+(?=b+)/';
#          └────┘
#         lookahead

testAll($r1, $tests);

आउटपुट है ( जैसा कि ideone.com पर देखा गया है ):

aaa 0
aaab 1 aaa
aaaxb 0
xaaab 0
b 0
abbb 1 a

यह बिल्कुल वैसा ही आउटपुट है जैसा हम चाहते हैं: हम मैच करते हैं a+, केवल अगर यह स्ट्रिंग की शुरुआत में है, और केवल तभी जब इसका तुरंत पालन किया जाता है b+

पाठ : मुखरता बनाने के लिए आप लुकअप में पैटर्न का उपयोग कर सकते हैं।


चरण 2: एक लुकहेड में कैप्चरिंग (और मुक्त - रिक्ति मोड)

अब हम कहते हैं कि भले ही हम b+मैच का हिस्सा नहीं बनना चाहते हैं , लेकिन हम इसे समूह 1 में वैसे भी कैद करना चाहते हैं । साथ ही, जैसा कि हम एक और अधिक जटिल पैटर्न होने का अनुमान लगाते हैं, हम फ्री-स्पेसिंग केx लिए संशोधक का उपयोग करते हैं ताकि हम हमारे regex को अधिक पठनीय बना सकते हैं।

हमारे पिछले PHP स्निपेट पर बिल्डिंग, अब हमारे पास निम्नलिखित पैटर्न हैं:

$r2 = '/ ^ a+ (?= (b+) ) /x';
#                └──┘ 
#                  1  
#             └────────┘
#              lookahead
 
testAll($r2, $tests);

अब आउटपुट ( जैसा कि ideone.com पर देखा गया है ):

aaa 0
aaab 1 aaa|b
aaaxb 0
xaaab 0
b 0
abbb 1 a|bbb

ध्यान दें कि जैसे प्रत्येक समूह द्वारा कैप्चर किए गए -ing aaa|bका परिणाम है । इस मामले में, समूह 0 (यानी पैटर्न क्या मिला) पर कब्जा कर लिया , और समूह 1 पर कब्जा कर लिया ।join'|'aaab

सबक : आप एक नज़र के अंदर कब्जा कर सकते हैं। पठनीयता बढ़ाने के लिए आप फ्री-स्पेस का उपयोग कर सकते हैं।


चरण 3: लुकहेड को "लूप" में बदलना

इससे पहले कि हम अपने गिनती तंत्र को पेश कर सकें, हमें अपने पैटर्न में एक संशोधन करना होगा। वर्तमान में, लुकहेड +पुनरावृत्ति "लूप" के बाहर है । यह अब तक ठीक है क्योंकि हम सिर्फ यह दावा करना चाहते थे कि b+हमारा निम्नलिखित है a+, लेकिन जो हम वास्तव में करना चाहते हैं वह यह दावा करता है कि प्रत्येक के लिए aहम "लूप" के अंदर मेल खाते हैं, इसके bसाथ जाने के लिए एक संगत है।

चलो अब के लिए गिनती तंत्र के बारे में चिंता न करें और केवल निम्नानुसार रीफैक्टरिंग करें:

  • सबसे पहले refactor a+करने के लिए (?: a )+(ध्यान दें कि (?:…)एक गैर पर कब्जा समूह है)
  • फिर इस गैर-कैप्चरिंग समूह के अंदर लुकहेड को स्थानांतरित करें
    • ध्यान दें कि अब हमें "छोड़ना" चाहिए, a*इससे पहले कि हम "देख" b+सकें, इसलिए तदनुसार पैटर्न को संशोधित करें

तो अब हमारे पास निम्नलिखित हैं:

$r3 = '/ ^ (?: a (?= a* (b+) ) )+ /x';
#                     └──┘  
#                       1   
#               └───────────┘ 
#                 lookahead   
#          └───────────────────┘
#           non-capturing group

आउटपुट पहले की तरह ही है ( जैसा कि ideone.com पर देखा गया है ), इसलिए उस संबंध में कोई बदलाव नहीं हुआ है। महत्वपूर्ण बात यह है कि अब हम पर जोर बना रहे है कि हर यात्रा के +"लूप"। हमारे वर्तमान पैटर्न के साथ, यह आवश्यक नहीं है, लेकिन अगले हम स्व-संदर्भ का उपयोग करके हमारे लिए समूह 1 "गणना" करेंगे।

पाठ : आप एक गैर-कैप्चरिंग समूह के अंदर कब्जा कर सकते हैं। लुकरॉइड्स को दोहराया जा सकता है।


चरण 4: यह वह चरण है जहां हम गिनती शुरू करते हैं

यहाँ हम क्या करने जा रहे हैं: हम समूह 1 को फिर से लिखेंगे:

  • के पहले पुनरावृत्ति के अंत में +, जब पहली aमिलान किया जाता है, तो इसे कैप्चर करना चाहिएb
  • दूसरी यात्रा के अंत में, जब एक और aमिलान किया जाता है, यह कब्जा करना चाहिएbb
  • तीसरे पुनरावृत्ति के अंत में, इसे कैप्चर करना चाहिए bbb
  • ...
  • एन- वें पुनरावृत्ति के अंत में , समूह 1 को बी एन पर कब्जा करना चाहिए
  • यदि bसमूह 1 में कब्जा करने के लिए पर्याप्त नहीं हैं तो जोर बस विफल हो जाता है

इसलिए समूह 1, जो अब है (b+), को कुछ इस तरह से फिर से लिखना होगा (\1 b)। यही है, हम bपिछले पुनरावृत्ति में किस समूह 1 पर कब्जा कर लेते हैं, इसके लिए "जोड़ने" का प्रयास करते हैं ।

यहां थोड़ी सी समस्या है कि यह पैटर्न "बेस केस" को याद कर रहा है, यानी वह मामला जहां वह बिना सेल्फ-रेफरेंस के मैच कर सकता है। एक बेस केस की आवश्यकता होती है क्योंकि समूह 1 "अनइंस्टाल्यूटेड" शुरू होता है; इसने अभी तक कुछ भी कब्जा नहीं किया है (एक खाली स्ट्रिंग भी नहीं), इसलिए एक आत्म-संदर्भ प्रयास हमेशा विफल रहेगा।

इसके आस-पास कई तरीके हैं, लेकिन अभी के लिए आइए हम आत्म-संदर्भ मिलान को वैकल्पिक बनाते हैं , यानी \1?। यह पूरी तरह से काम कर सकता है या नहीं भी कर सकता है, लेकिन चलो बस देखते हैं कि क्या करता है, और अगर कोई समस्या है तो हम उस पुल को पार करेंगे जब हम उसके पास आएंगे। इसके अलावा, हम कुछ और परीक्षण मामलों को जोड़ देंगे।

$tests = array(
  'aaa', 'aaab', 'aaaxb', 'xaaab', 'b', 'abbb', 'aabb', 'aaabbbbb', 'aaaaabbb'
);
 
$r4 = '/ ^ (?: a (?= a* (\1? b) ) )+ /x';
#                     └─────┘ | 
#                        1    | 
#               └──────────────┘ 
#                   lookahead    
#          └──────────────────────┘
#             non-capturing group

अब आउटपुट ( जैसा कि ideone.com पर देखा गया है ):

aaa 0
aaab 1 aaa|b        # (*gasp!*)
aaaxb 0
xaaab 0
b 0
abbb 1 a|b          # yes!
aabb 1 aa|bb        # YES!!
aaabbbbb 1 aaa|bbb  # YESS!!!
aaaaabbb 1 aaaaa|bb # NOOOOOoooooo....

ए-हा! ऐसा लगता है कि हम वास्तव में अब समाधान के करीब हैं! हम स्व-संदर्भ का उपयोग करके समूह 1 को "गणना" करने में कामयाब रहे! लेकिन रुकिए… दूसरे और आखिरी टेस्ट के मामलों में कुछ गड़बड़ है !! वहाँ पर्याप्त नहीं हैं b, और किसी तरह यह गलत गिना! हम जांच करेंगे कि यह अगले चरण में क्यों हुआ।

पाठ : स्व-संदर्भ समूह को "इनिशियलाइज़" करने का एक तरीका सेल्फ-रेफरेंस मैचिंग को वैकल्पिक बनाना है।


चरण 4 Step: यह समझना कि क्या गलत हुआ

समस्या यह है कि चूंकि हमने स्व-संदर्भ मिलान को वैकल्पिक बनाया है, इसलिए "काउंटर" 0 पर वापस "रीसेट" कर सकता है जब पर्याप्त नहीं हैं b। आइए aaaaabbbइनपुट के साथ हमारे पैटर्न के प्रत्येक पुनरावृत्ति पर क्या होता है, इसकी बारीकी से जांच करें ।

 a a a a a b b b

# Initial state: Group 1 is "uninitialized".
           _
 a a a a a b b b
  
  # 1st iteration: Group 1 couldn't match \1 since it was "uninitialized",
  #                  so it matched and captured just b
           ___
 a a a a a b b b
    
    # 2nd iteration: Group 1 matched \1b and captured bb
           _____
 a a a a a b b b
      
      # 3rd iteration: Group 1 matched \1b and captured bbb
           _
 a a a a a b b b
        
        # 4th iteration: Group 1 could still match \1, but not \1b,
        #  (!!!)           so it matched and captured just b
           ___
 a a a a a b b b
          
          # 5th iteration: Group 1 matched \1b and captured bb
          #
          # No more a, + "loop" terminates

ए-हा! हमारे चौथे पुनरावृत्ति पर, हम अभी भी मैच \1कर सकते हैं, लेकिन हम मेल नहीं खा सकते हैं \1b! चूंकि हम स्व-संदर्भ मिलान के साथ वैकल्पिक होने की अनुमति देते हैं \1?, इंजन बैकट्रैक करता है और "नो थैंक्स" विकल्प लेता है, जो तब हमें मिलान करने और बस पकड़ने की अनुमति देता है b!

हालांकि, ध्यान दें, कि बहुत पहले पुनरावृत्ति को छोड़कर, आप हमेशा केवल आत्म-संदर्भ से मेल खा सकते हैं \1। यह स्पष्ट है, ज़ाहिर है, क्योंकि यह वही है जिसे हमने अपने पिछले पुनरावृत्ति पर कब्जा कर लिया है, और हमारे सेटअप में हम हमेशा इसे फिर से मेल कर सकते हैं (जैसे कि अगर हमने bbbपिछली बार कब्जा कर लिया है , तो हम गारंटी देते हैं कि अभी भी होगा bbb, लेकिन हो सकता है या bbbbयह समय नहीं हो सकता है )।

पाठ : बैकट्रैकिंग से सावधान रहें। रेगेक्स इंजन दिए गए पैटर्न से मेल खाने तक आपको उतना ही पीछे ले जाने की अनुमति देगा। यह प्रदर्शन (यानी भयावह बैकट्रैकिंग ) और / या शुद्धताको प्रभावित कर सकता है।


चरण 5: बचाव के लिए आत्म-कब्जे!

"ठीक" अब स्पष्ट होना चाहिए: के साथ वैकल्पिक पुनरावृत्ति गठबंधन अधिकार परिमाणक। यही है, बस के बजाय, इसके बजाय का ?उपयोग करें ?+(याद रखें कि एक पुनरावृत्ति जो कि अधिकार के रूप में परिमाणित है, पीछे नहीं हटती है, भले ही इस तरह के "सहयोग" के परिणामस्वरूप समग्र पैटर्न का एक मैच हो सकता है)।

बहुत ही अनौपचारिक शब्दों में, यह वही है ?+, ?और ??कहते हैं:

?+

  • (वैकल्पिक) "यह वहाँ नहीं है,"
    • (अधिकारी) "लेकिन अगर यह वहाँ है, तो आपको इसे लेना चाहिए और जाने नहीं देना चाहिए!"

?

  • (वैकल्पिक) "यह वहाँ नहीं है,"
    • (लालची) "लेकिन अगर यह है तो आप इसे अभी के लिए ले सकते हैं"
      • (पीछे) "लेकिन आपको इसे बाद में जाने देने के लिए कहा जा सकता है!"

??

  • (वैकल्पिक) "यह वहाँ नहीं है,"
    • (अनिच्छुक) "और यहां तक ​​कि अगर यह है कि आपको इसे अभी तक नहीं लेना है,"
      • (पीछे) "लेकिन आपको इसे बाद में लेने के लिए कहा जा सकता है!"

हमारे सेटअप में, \1पहली बार ऐसा नहीं होगा, लेकिन यह हमेशा उसके बाद किसी भी समय होगा, और हम हमेशा इसे फिर से मेल खाना चाहते हैं। इस प्रकार, \1?+वास्तव में हम जो चाहते हैं वह पूरा होगा।

$r5 = '/ ^ (?: a (?= a* (\1?+ b) ) )+ /x';
#                     └──────┘  
#                         1     
#               └───────────────┘ 
#                   lookahead     
#          └───────────────────────┘
#             non-capturing group

अब आउटपुट है ( जैसा कि ideone.com पर देखा गया है ):

aaa 0
aaab 1 a|b          # Yay! Fixed!
aaaxb 0
xaaab 0
b 0
abbb 1 a|b
aabb 1 aa|bb
aaabbbbb 1 aaa|bbb
aaaaabbb 1 aaa|bbb  # Hurrahh!!!

Voilà !!! समस्या सुलझ गयी!!! हम अब ठीक से गिन रहे हैं, ठीक उसी तरह जिस तरह से हम चाहते हैं!

पाठ : लालची, अनिच्छुक, और अधिकारपूर्ण दोहराव के बीच अंतर जानें। वैकल्पिक-पास एक शक्तिशाली संयोजन हो सकता है।


चरण 6: स्पर्श को खत्म करना

तो अभी जो हमारे पास है वह एक पैटर्न है जो aबार-बार मेल खाता है , और प्रत्येक के लिए aजो मिलान किया गया था, वहाँ bसमूह 1 में एक संगत कैप्चर किया गया है। +जब कोई अधिक नहीं होता है a, तो टर्मिनेट होता है , या यदि इसके bलिए कोई संगत नहीं है एक a

काम खत्म करने के लिए, हमें बस अपने पैटर्न को जोड़ने की जरूरत है \1 $। यह अब एक बैक सन्दर्भ है कि किस समूह 1 का मिलान हुआ, उसके बाद लाइन एंकर का अंत हुआ। लंगर सुनिश्चित करता है कि bस्ट्रिंग में कोई अतिरिक्त नहीं है ; दूसरे शब्दों में, वास्तव में हमारे पास है कि एक एन बी एन

यहां अंतिम रूप दिया गया पैटर्न, अतिरिक्त परीक्षण मामलों के साथ, जिसमें 10,000 वर्ण लंबा है:

$tests = array(
  'aaa', 'aaab', 'aaaxb', 'xaaab', 'b', 'abbb', 'aabb', 'aaabbbbb', 'aaaaabbb',
  '', 'ab', 'abb', 'aab', 'aaaabb', 'aaabbb', 'bbbaaa', 'ababab', 'abc',
  str_repeat('a', 5000).str_repeat('b', 5000)
);
 
$r6 = '/ ^ (?: a (?= a* (\1?+ b) ) )+ \1 $ /x';
#                     └──────┘  
#                         1     
#               └───────────────┘ 
#                   lookahead     
#          └───────────────────────┘
#             non-capturing group

: यह 4 मैचों पाता है ab, aabb, aaabbb, और एक 50005000Ideone.com पर इसे चलाने के लिए केवल 0.06s लगते हैं ।


चरण 7: जावा परीक्षण

तो पैटर्न PHP में काम करता है, लेकिन अंतिम लक्ष्य एक पैटर्न लिखना है जो जावा में काम करता है।

public static void main(String[] args) {
 
        String aNbN = "(?x) (?:  a  (?= a* (\\1?+ b))  )+ \\1";
        String[] tests = {
                "",      // false
                "ab",    // true
                "abb",   // false
                "aab",   // false
                "aabb",  // true
                "abab",  // false
                "abc",   // false
                repeat('a', 5000) + repeat('b', 4999), // false
                repeat('a', 5000) + repeat('b', 5000), // true
                repeat('a', 5000) + repeat('b', 5001), // false
        };
        for (String test : tests) {
                System.out.printf("[%s]%n  %s%n%n", test, test.matches(aNbN));
        }
 
}
 
static String repeat(char ch, int n) {
        return new String(new char[n]).replace('\0', ch);
}

पैटर्न उम्मीद के मुताबिक काम करता है ( जैसा कि ideone.com पर देखा गया है )।


और अब हम निष्कर्ष पर आते हैं ...

यह कहने की ज़रूरत है कि a*लुकहेड में, और वास्तव में "मुख्य +लूप", दोनों बैकट्रैकिंग की अनुमति देते हैं। पाठकों को यह पुष्टि करने के लिए प्रोत्साहित किया जाता है कि यह शुद्धता के संदर्भ में समस्या क्यों नहीं है, और क्यों एक ही समय में दोनों के पास रखने से भी काम चल जाएगा (हालांकि एक ही पैटर्न में अनिवार्य और गैर-अनिवार्य अधिकारी मात्रात्मक मिश्रण करना गलतफहमी पैदा कर सकता है)।

यह भी कहा जा सकता है कि जब यह एक regex पैटर्न है कि पूर्ति करेंगे, यह है कि साफ है एक एन बी एन , इस अभ्यास में हमेशा नहीं "सर्वश्रेष्ठ" समाधान में है। एक बेहतर समाधान बस मैच करना है ^(a+)(b+)$, और फिर होस्टिंग प्रोग्रामिंग भाषा में समूहों 1 और 2 द्वारा पकड़े गए तार की लंबाई की तुलना करें।

PHP में, यह कुछ इस तरह दिख सकता है ( जैसा कि ideone.com में देखा गया है ):

function is_anbn($s) {
   return (preg_match('/^(a+)(b+)$/', $s, $groups)) &&
      (strlen($groups[1]) == strlen($groups[2]));
}

इस लेख का उद्देश्य पाठकों को यह समझाने के लिए नहीं है कि रेगेक्स लगभग कुछ भी कर सकता है; यह स्पष्ट रूप से नहीं हो सकता है, और यहां तक ​​कि उन चीजों के लिए भी, जो होस्टिंग भाषा में कम से कम आंशिक प्रतिनिधिमंडल माना जाना चाहिए, अगर यह एक सरल समाधान की ओर जाता है।

जैसा कि शीर्ष पर उल्लेख किया गया है, जबकि यह आलेख आवश्यक रूप से स्टैकओवरफ़्लो के [regex]लिए टैग किया गया है, यह संभवतः इससे अधिक है। हालांकि निश्चित रूप से मुखरता, नेस्टेड रेफरेंस, प्रिवेंटिव क्वांटिफायर इत्यादि के बारे में सीखने में अहमियत है, शायद यहाँ का बड़ा सबक रचनात्मक प्रक्रिया है जिसके द्वारा समस्याओं को हल करने की कोशिश की जा सकती है, दृढ़ संकल्प और कड़ी मेहनत जो कि अक्सर आपके अधीन होती है। विभिन्न बाधाओं, एक कार्य समाधान का निर्माण करने के लिए विभिन्न भागों से व्यवस्थित रचना, आदि।


बोनस सामग्री! पीसीआरई पुनरावर्ती पैटर्न!

चूंकि हमने पीएचपी को लाया था, इसलिए यह कहा जाना चाहिए कि पीसीआरई पुनरावर्ती पैटर्न और सबरूटीन्स का समर्थन करता है। इस प्रकार, निम्नलिखित पैटर्न के लिए काम करता है preg_match( जैसा कि ideone.com पर देखा गया है ):

$rRecursive = '/ ^ (a (?1)? b) $ /x';

वर्तमान में जावा का रेगेक्स पुनरावर्ती पैटर्न का समर्थन नहीं करता है।


और भी अधिक बोनस सामग्री! मैचिंग एन बी एन सी एन !!

तो हमने देखा कि कैसे मैच के लिए एक nn जो गैर नियमित रूप से है, लेकिन अभी भी विषय से मुक्त है, लेकिन हम भी मिलान कर सकते हैं एक एन बी एन सी एन , जो भी विषय से मुक्त नहीं है?

जवाब है, हां, हाँ! पाठकों को इसे स्वयं हल करने का प्रयास करने के लिए प्रोत्साहित किया जाता है, लेकिन समाधान नीचे दिया गया है ( ideone.com पर जावा में कार्यान्वयन के साथ )।

^ (?: a (?= a* (\1?+ b) b* (\2?+ c) ) )+ \1 \2 $


इसमें कोई संदेह नहीं है कि इस लंबे उत्तर में गलतियां / टाइपो हो सकती हैं, इसलिए कृपया टिप्पणियों के रूप में प्रतिक्रियाएं छोड़ दें ताकि मैं उन्हें अपने आप संशोधित कर सकूं।
पॉलिनेजेलिकूबेनेन्ट

अच्छा काम। मुझे पढ़ने में थोड़ा समय लगेगा, लेकिन मूल रूप से अंतिम पंक्ति को पढ़ना असंभव है; यह इतना छोटा फ़ॉन्ट है। ------ अरे रुको। क्या featureयह एक अच्छा विचार है? मुझे पता है कि अंतिम चिन्ह क्या है, लेकिन इसे पढ़ा नहीं जा सकता (इसे कॉपी पेस्ट करने से अलग)।
पीटर अज़ताई

6
@Peter: छोटे पाठ को हाइलाइट करें और कॉपी करें और किसी अन्य चीज़ में पेस्ट करें। यह उद्देश्य पर पढ़ना मुश्किल है: यह एक बिगाड़ने वाला है, बोनस पहेली का समाधान।
पॉलीजेन लुब्रीकेंट्स

8
+1: शानदार व्याख्या, ये "उन्नत लेख" शानदार विचार हैं।
कैलम रोजर्स

1
@LarsH PHP के preg_match()का एक उदाहरण है PCRE । Java regexes Perl regexps के पुराने संस्करण पर आधारित प्रतीत होता है । जिसका अर्थ है कि जावा में संस्करण की तुलना में PHP रेगेक्स अधिक शक्तिशाली हैं। के रूप में 2013-02-21 , pcre.txt कहा गया है कि यह पर्ल 5.12 के साथ लगभग मेल खाती है । जबकि पर्ल फिलहाल 5.16 पर है, जबकि 5.18 कुछ महीने की छूट है। (वास्तव में उस समय में रेगेक्स में बहुत कुछ नहीं जोड़ा गया है)
ब्रैड गिल्बर्ट

20

यह देखते हुए कि पीसीआरई का समर्थन करने वाले पैटर्न का कोई उल्लेख नहीं किया गया है, मैं सिर्फ पीसीआरई का सबसे सरल और सबसे कुशल उदाहरण बताना चाहूंगा जो भाषा में प्रश्न का वर्णन करता है:

/^(a(?1)?b)$/

+1 वाह, मुझे नहीं पता था कि पीसीआरई पुनरावर्ती पैटर्न का समर्थन करता है (मैं अभी भी सीख रहा हूं! हर दिन!)। मैंने इस जानकारी को समायोजित करने के लिए लेख को संशोधित किया है। मुझे नहीं लगता कि पुनरावर्ती पैटर्न मेल खा सकता है a^n b^n c^n, हालांकि।
polygenelubricants

यह ध्यान दिया जाना चाहिए कि यह विकल्प सरल है, लेकिन पोस्ट किए गए उत्तर के रूप में अच्छा नहीं है - लंबे तारों पर पुनरावृत्ति अधिक हो जाती है।
कोबी

@ कोबी यह "अच्छा" की आपकी परिभाषा पर निर्भर करता है। उदाहरण के लिए पुनरावर्ती समाधान परिमाण के एक क्रम के आसपास अन्य ( codepad.viper-7.com/CWgy7c ) की तुलना में तेजी से होता है । और इसे समझना बहुत आसान है। पुनरावर्ती समाधान व्याकरण का प्रत्यक्ष रूप से एक रेगेक्स में बहुत परिवर्तन है (वास्तव में आप इसे केवल व्याकरणिक रूप में लिख सकते हैं, यह काम करेगा)।
निकी सी

1
@polygeniclubricants, आप उस पैटर्न को दो पुनरावर्ती पैटर्न के साथ मिला सकते हैं, एक तो कैप्चरिंग के बिना as और bs का उपभोग करने के लिए (और पुष्टि करता है कि समान राशि w / recursion हैं), उसके बाद कैप्चरिंग regex है जो लालच से सभी को खाता है, और फिर पुनरावर्ती को लागू करता है पैटर्न का उपभोग करने और सत्यापित करने के लिए कि bएस और cएस की समान संख्या है । Regex है: /^(?=(a(?-1)?b)c)a+(b(?-1)?c)$/x। क्रेडिट करने के लिए: nikic.github.io/2012/06/15/...
जोश Reback

11

के रूप में प्रश्न में आपका उल्लेख - नेट समूह, प्रकार के पैटर्न में संतुलन के साथ एक एन बी एन सी एन डी एन ... z n आसानी से के रूप में मिलान किया जा सकता

^
  (?<A>a)+
  (?<B-A>b)+  (?(A)(?!))
  (?<C-B>c)+  (?(B)(?!))
  ...
  (?<Z-Y>z)+  (?(Y)(?!))
$

उदाहरण के लिए: http://www.ideone.com/usuOE


संपादित करें:

पुनरावर्ती पैटर्न के साथ सामान्यीकृत भाषा के लिए एक पीसीआरई पैटर्न भी है, लेकिन एक लुकहेड की आवश्यकता है। मुझे नहीं लगता कि यह ऊपर का सीधा अनुवाद है।

^
  (?=(a(?-1)?b))  a+
  (?=(b(?-1)?c))  b+
  ...
  (?=(x(?-1)?y))  x+
     (y(?-1)?z)
$

उदाहरण के लिए: http://www.ideone.com/9gUwF


1
@ पाली: धन्यवाद :)। वास्तव में मैं .NET पैटर्न से परिचित नहीं हूं, लेकिन इस तरह के पैटर्न के लिए यह संतुलन समूहों के साथ बहुत आसान हो जाता है, इसलिए मैं इस उत्तर को पूरक करता हूं।
kennytm

क्या आप पुनरावर्ती पैटर्न के साथ ऐसा कर सकते हैं? क्योंकि यदि आप नहीं कर सकते हैं, तो यह एक दिलचस्प मोड़ है जो समूह को संतुलित कर सकता है जो पुनरावर्ती पैटर्न नहीं कर सकता है। (और हां, मैं पूरक की बहुत सराहना करता हूं)।
पॉलीजेन लुब्रिकेंट्स

वैसे, कारण है कि मैंने .NET समाधान को छोड़ दिया था क्योंकि मेरे पास "हम कैसे मेल कर सकते हैं" की योजना है a^n b^n .NET regex के साथ ? भविष्य में लेख, लेकिन यदि आप चाहें तो इसे लिखने के लिए आपका स्वागत है। मैं ये लेख सिर्फ अपने लिए नहीं कर रहा हूँ; मैं दूसरों को इसे करने के लिए प्रोत्साहित करना चाहता हूं और साथ ही साइट पर अच्छी सामग्री रखना चाहता हूं।
पॉलीजेन लुब्रिकेंट

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

1
मैं केवल यह बताना चाहूंगा कि इस पैटर्न का पीसीआरई संस्करण थोड़ा त्रुटिपूर्ण है क्योंकि यह मेल खाता है यदि वर्णों का अगला हिस्सा पिछले से अधिक लंबा है। यहाँ देखें: regex101.com/r/sdlRTm/1 जोड़ने के लिए आप की जरूरत है (?!b), (?!c)आदि तो जैसे कैप्चर समूहों के बाद: regex101.com/r/sdlRTm/2
jaytea
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.