जवाब है, कहने की जरूरत नहीं है, हाँ! आप निश्चित रूप से एक 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
'|'
aaa
b
सबक : आप एक नज़र के अंदर कब्जा कर सकते हैं। पठनीयता बढ़ाने के लिए आप फ्री-स्पेस का उपयोग कर सकते हैं।
चरण 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
, और एक 5000 ख 5000 । Ideone.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';
वर्तमान में जावा का रेगेक्स पुनरावर्ती पैटर्न का समर्थन नहीं करता है।
और भी अधिक बोनस सामग्री! मैचिंग ए एन बी एन सी एन !!
तो हमने देखा कि कैसे मैच के लिए एक n ख n जो गैर नियमित रूप से है, लेकिन अभी भी विषय से मुक्त है, लेकिन हम भी मिलान कर सकते हैं एक एन बी एन सी एन , जो भी विषय से मुक्त नहीं है?
जवाब है, हां, हाँ! पाठकों को इसे स्वयं हल करने का प्रयास करने के लिए प्रोत्साहित किया जाता है, लेकिन समाधान नीचे दिया गया है ( ideone.com पर जावा में कार्यान्वयन के साथ )।
^ (?: a (?= a* (\1?+ b) b* (\2?+ c) ) )+ \1 \2 $