जहाँ तक मुझे पता है, .NET के regex फ्लेवर के लिए बैलेंसिंग ग्रुप्स अद्वितीय हैं।
एक तरफ: बार-बार समूह
सबसे पहले, आपको यह जानना होगा कि .NET है (फिर, जहाँ तक मुझे पता है) एकमात्र रेगीक्स स्वाद है जो आपको एक कैप्चरिंग ग्रुप के कई कैप्चर एक्सेस करने देता है (बैकरेफेर में नहीं लेकिन मैच पूरा होने के बाद)।
एक उदाहरण के साथ यह समझाने के लिए, पैटर्न पर विचार करें
(.)+
और स्ट्रिंग "abcd"
।
अन्य सभी रेगेक्स फ्लेवर में, कैप्चरिंग ग्रुप 1
केवल एक परिणाम देगा: d
(ध्यान दें, पूर्ण मैच निश्चित abcd
रूप से अपेक्षित होगा)। ऐसा इसलिए है क्योंकि कैप्चरिंग ग्रुप का हर नया उपयोग पिछली कैप्चर को ओवरराइट कर देता है।
दूसरी ओर .NET उन सभी को याद करता है। और यह एक स्टैक में ऐसा करता है। उपरोक्त रेगेक्स की तरह मिलान करने के बाद
Match m = new Regex(@"(.)+").Match("abcd");
तुम पाओगे कि
m.Groups[1].Captures
एक है CaptureCollection
चार कैप्चर करने के लिए जिसका तत्वों अनुरूप
0: "a"
1: "b"
2: "c"
3: "d"
जहां संख्या सूचकांक में है CaptureCollection
। इसलिए मूल रूप से हर बार जब समूह का फिर से उपयोग किया जाता है, तो एक नया कब्जा स्टैक पर धकेल दिया जाता है।
यह और अधिक दिलचस्प हो जाता है अगर हम नामांकित समूहों का उपयोग कर रहे हैं। क्योंकि .NET उसी नाम का बार-बार उपयोग करने की अनुमति देता है, जैसे हम एक regex लिख सकते हैं
(?<word>\w+)\W+(?<word>\w+)
एक ही समूह में दो शब्दों को पकड़ने के लिए। फिर, हर बार एक निश्चित नाम वाला एक समूह सामने आता है, एक कैप्चर को उसके स्टैक पर धकेल दिया जाता है। तो इस रेगेक्स को इनपुट पर लागू करना "foo bar"
और निरीक्षण करना
m.Groups["word"].Captures
हम दो कैद पाते हैं
0: "foo"
1: "bar"
यह हमें अभिव्यक्ति के विभिन्न हिस्सों से एक ही ढेर पर चीजों को धक्का देने की भी अनुमति देता है। लेकिन फिर भी, यह केवल .NET की विशेषता है जो कई कैप्चर को ट्रैक करने में सक्षम है जो इसमें सूचीबद्ध हैं CaptureCollection
। लेकिन मैंने कहा, यह संग्रह एक ढेर है । तो क्या हम इससे चीजों को पॉप कर सकते हैं ?
दर्ज करें: संतुलन समूह
यह पता चलता है कि हम कर सकते हैं। यदि हम जैसे एक समूह का उपयोग करते हैं (?<-word>...)
, तो अंतिम कैप्चर को स्टैक से पॉपअप किया जाता है word
यदि उपप्रकारकता ...
मेल खाता है। इसलिए अगर हम अपनी पिछली अभिव्यक्ति को बदलते हैं
(?<word>\w+)\W+(?<-word>\w+)
फिर दूसरा समूह पहले समूह के कैप्चर को पॉप करेगा, और हम CaptureCollection
अंत में एक खाली प्राप्त करेंगे । बेशक, यह उदाहरण बहुत बेकार है।
लेकिन माइनस-सिंटैक्स के लिए एक और विवरण है: यदि स्टैक पहले से ही खाली है, तो समूह विफल रहता है (इसके उप-संस्करण की परवाह किए बिना)। हम घोंसले के स्तर की गणना करने के लिए इस व्यवहार का लाभ उठा सकते हैं - और यह वह जगह है जहां नाम संतुलन समूह आता है (और जहां यह दिलचस्प हो जाता है)। कहते हैं कि हम उन तारों का मिलान करना चाहते हैं जो सही ढंग से कोष्ठक हैं। हम स्टैक पर प्रत्येक उद्घाटन कोष्ठक को धक्का देते हैं, और प्रत्येक बंद कोष्ठक के लिए एक कैप्चर करते हैं। यदि हम एक बंद कोष्ठक को बहुत अधिक से सामना करते हैं, तो यह एक खाली स्टैक को पॉप करने की कोशिश करेगा और पैटर्न को विफल कर देगा:
^(?:[^()]|(?<Open>[(])|(?<-Open>[)]))*$
इसलिए हमारे पास एक पुनरावृत्ति में तीन विकल्प हैं। पहला विकल्प सब कुछ खा जाता है जो कोष्ठक नहीं है। (
स्टैक पर उन्हें धकेलते हुए दूसरा वैकल्पिक मैच । )
स्टैक से तत्वों को पॉप करते समय तीसरा वैकल्पिक मिलान होता है (यदि संभव हो!)।
नोट: बस स्पष्ट करने के लिए, हम केवल जाँच रहे हैं कि कोई बेजोड़ कोष्ठक नहीं हैं! इसका मतलब है कि सब पर कोई कोष्ठकों युक्त स्ट्रिंग होगा , से मेल खाते हैं क्योंकि वे अभी भी वाक्य रचना मान्य हैं (कुछ वाक्य रचना जहां मैच करने के लिए अपने कोष्ठकों की जरूरत है)। यदि आप कोष्ठक के कम से कम एक सेट को सुनिश्चित करना चाहते हैं, तो बस इसके (?=.*[(])
बाद एक लुकहेड जोड़ें ^
।
यह पैटर्न हालांकि सही (या पूरी तरह सही) नहीं है।
समापन: सशर्त पैटर्न
एक और पकड़ है: यह सुनिश्चित नहीं करता है कि स्ट्रिंग के अंत में स्टैक खाली है (इसलिए (foo(bar)
मान्य होगा)। .NET (और कई अन्य स्वादों) में एक और निर्माण है जो हमें यहां मदद करता है: सशर्त पैटर्न। सामान्य वाक्यविन्यास है
(?(condition)truePattern|falsePattern)
जहां falsePattern
वैकल्पिक है - अगर इसे छोड़ दिया जाता है तो झूठे केस हमेशा मेल खाएगा। हालत या तो एक पैटर्न, या एक कैप्चरिंग ग्रुप का नाम हो सकता है। मैं यहाँ बाद वाले मामले पर ध्यान केंद्रित करूँगा। यदि यह एक कैप्चरिंग ग्रुप का नाम है, तो truePattern
इसका उपयोग तब किया जाता है जब और केवल उस विशेष समूह के लिए कैप्चर स्टैक खाली न हो। यही है, एक सशर्त पैटर्न (?(name)yes|no)
पढ़ता है "अगर name
मिलान किया है और कुछ पर कब्जा कर लिया है (जो अभी भी स्टैक पर है), पैटर्न का yes
उपयोग करें अन्यथा पैटर्न no
"।
इसलिए हमारे उपरोक्त पैटर्न के अंत में हम कुछ ऐसा जोड़ सकते हैं (?(Open)failPattern)
जिसके कारण संपूर्ण पैटर्न विफल हो जाता है, अगर Open
-स्टैक खाली नहीं है। पैटर्न को बिना शर्त विफल बनाने के लिए सबसे सरल बात है (?!)
(एक खाली नकारात्मक लुकहेड)। तो हमारे पास हमारा अंतिम पैटर्न है:
^(?:[^()]|(?<Open>[(])|(?<-Open>[)]))*(?(Open)(?!))$
ध्यान दें कि इस सशर्त सिंटैक्स का संतुलन समूहों के साथ कुछ भी नहीं है, लेकिन उनकी पूरी शक्ति का उपयोग करना आवश्यक है।
यहाँ से, आकाश की सीमा है। कई बहुत परिष्कृत उपयोग संभव हैं और चर-लंबाई के रूप में अन्य .NET-Regex सुविधाओं के साथ संयोजन में उपयोग किए जाने पर कुछ गोच हो जाते हैं ( जो मुझे स्वयं कठिन तरीका सीखना था )। हालांकि मुख्य सवाल यह है कि क्या इन सुविधाओं का उपयोग करते समय आपका कोड अभी भी बनाए रखा जा सकता है? आपको इसे वास्तव में अच्छी तरह से प्रलेखित करने की आवश्यकता है, और सुनिश्चित करें कि हर कोई जो इस पर काम करता है, वह इन विशेषताओं के बारे में भी जानता है। अन्यथा आप बेहतर बंद हो सकते हैं, बस स्ट्रिंग को मैन्युअल रूप से चरित्र-दर-चरित्र चलना और एक पूर्णांक में घोंसले के शिकार के स्तर की गणना करना।
परिशिष्ट: (?<A-B>...)
वाक्य रचना के साथ क्या है ?
इस भाग का श्रेय कोबी को जाता है (अधिक विवरण के लिए नीचे उसका उत्तर देखें)।
अब उपरोक्त सभी के साथ, हम पुष्टि कर सकते हैं कि एक स्ट्रिंग सही ढंग से कोष्ठक है। लेकिन यह बहुत अधिक उपयोगी होगा, अगर हम उन सभी कोष्ठकों की सामग्री के लिए वास्तव में प्राप्त कर सकते हैं (नेस्टेड)। बेशक, हम एक अलग कैप्चर स्टैक में कोष्ठक खोलने और बंद करने को याद कर सकते हैं जो खाली नहीं है, और फिर एक अलग चरण में उनके स्थान के आधार पर कुछ प्रतिस्थापन निष्कर्षण करते हैं।
लेकिन .NET यहां एक और सुविधा सुविधा प्रदान करता है: यदि हम उपयोग करते हैं (?<A-B>subPattern)
, तो न केवल स्टैक से कैप्चर किया गया पॉप है B
, बल्कि पॉप-अप कैप्चर के बीच सब कुछ है B
और यह वर्तमान समूह स्टैक पर धकेल दिया जाता है A
। इसलिए यदि हम समापन कोष्ठक के लिए इस तरह के एक समूह का उपयोग करते हैं, तो हमारे स्टैक से घोंसले के स्तर को पॉप करते हुए, हम जोड़ी की सामग्री को दूसरे स्टैक पर भी धकेल सकते हैं:
^(?:[^()]|(?<Open>[(])|(?<Content-Open>[)]))*(?(Open)(?!))$
कोबी ने अपने जवाब में यह लाइव-डेमो प्रदान किया
इसलिए इन सभी चीजों को एक साथ लेकर हम कर सकते हैं:
- मनमाने ढंग से कई कैद याद रखें
- नेस्टेड संरचनाओं को मान्य करें
- प्रत्येक घोंसले के स्तर पर कब्जा
सभी एक ही नियमित अभिव्यक्ति में। यदि यह रोमांचक नहीं है ...);
जब मैंने पहली बार उनके बारे में सीखा तो कुछ संसाधन मुझे मददगार लगे: