रेगेक्स के व्यावहारिक पैटर्न: एंकर, क्वांटिफ़ायर और कैप्चर ग्रुप

2026-04-13 को प्रकाशित 8 मिनट पढ़ें

सारांश (TL;DR)

2 जुलाई 2019 को Cloudflare केवल एक रेगेक्स की वजह से 27 मिनट तक वैश्विक ट्रैफ़िक के लगभग 50% में गिरावट में डूब गया — उनका WAF एक नए नियम में .*(?:.*=.*) जैसा लगभग मैच करके फ़ेल होने वाला पैटर्न लेकर आया था। बैकट्रैकिंग विस्फोट हुआ, सिंगल कोर 100% पर अटक गया, और सारे रिक्वेस्ट रुक गए। वह घटना रेगेक्स के बारे में जो सबसे सीधी बात सिखाती है, वह यह है: रेगेक्स अपनी लागत अच्छी तरह छिपाता है। आमतौर पर यह छोटा, तेज़ और आकर्षक दिखता है, लेकिन “एक ग़लत इनपुट पंक्ति” एक पूरे सिस्टम को रोक सकती है। इसलिए इस लेख का लहजा थोड़ा संशयवादी है। व्यावहारिक रेगेक्स कुछ बुनियादी ईंटों पर बना है: शुरुआत·अंत·शब्द-सीमा सेट करने वाले एंकर, “इन अक्षरों में से एक” वर्णन करने वाले कैरेक्टर क्लास, “कितनी बार” तय करने वाले क्वांटिफ़ायर, और कैप्चर·संदर्भ·विकल्प के लिए ग्रुप। सही उपयोग में ईमेल का मोटा मैच, लॉग से फ़ील्ड निकालना, फ़ोन नंबर सामान्यीकरण जैसे काम छोटे पैटर्न से होते हैं। ग़लत उपयोग में बहुत ज़्यादा या बहुत कम मैच होता है, या ऊपर की घटना की तरह इंजन जाम हो जाता है। HTML, JSON या XML को रेगेक्स से पार्स मत करें। नियमित भाषाएँ संतुलित नेस्टिंग का वर्णन नहीं कर सकतीं। इंजन-दर-इंजन बड़े अंतर हैं — ECMAScript, PCRE (Perl/PHP), Python re, Go RE2 — लुकअराउंड, बैकरेफ़्रेंस, यूनिकोड में। Cloudflare की घटना PCRE के बैकट्रैकिंग मॉडल की वजह से थी (Go RE2 होती तो नहीं होती)।

पृष्ठभूमि

रेगेक्स स्ट्रिंग मैचिंग के लिए एक छोटा grammar है। अक्सर इस्तेमाल होने वाले टुकड़े नाम से अलग करें तो समझ साफ़ होती है।

एंकर अक्षर नहीं खाते, स्थिति दावा करते हैं। ^ इनपुट की शुरुआत (मल्टीलाइन मोड में पंक्ति की शुरुआत), $ अंत, \b शब्द-सीमा। बिना एंकर abc किसी भी लंबी स्ट्रिंग में कहीं भी मैच कर सकता है, पर ^abc$ केवल तब जब पूरी स्ट्रिंग ही abc हो।

कैरेक्टर क्लास एक अक्षर के “किस सेट में है” को बताते हैं। [abc] a, b, c में से कोई; [a-z] छोटे अक्षर; [^0-9] “जो अंक नहीं है”। आम संक्षेप: \d अंक, \w शब्द अक्षर, \s whitespace। बड़े अक्षर वाले \D, \W, \S उनके पूरक हैं। . डिफ़ॉल्ट रूप से न्यू-लाइन छोड़कर कुछ भी मैच करता है; s (dotall) फ़्लैग यह बदलता है।

क्वांटिफ़ायर पिछले टोकन से जुड़कर दोहराव गिनते हैं। * शून्य या अधिक, + एक या अधिक, ? शून्य या एक, {n,m} “n से m बार”। डिफ़ॉल्ट greedy है: जितना संभव मैच, फिर पीछे फ़ेल हो तो एक-एक कम करके। ? जोड़ने पर lazy*?, +?, ?? — कम से कम मैच, पीछे की माँग हो तो बढ़ाता है। कुछ इंजन possessive क्वांटिफ़ायर (*+, ++) समर्थन देते हैं जो greedy मैच करते हैं पर पीछे हटते नहीं। यही Cloudflare वाले मामले में ग़ायब टूल था।

ग्रुप उप-पैटर्न लपेटते हैं। (pattern) कैप्चर ग्रुप है, जिसमें खुले कोष्ठक के क्रम से 1 से गिनती शुरू होती है, और पैटर्न में \1 से, होस्ट भाषा में इंडेक्स से संदर्भित किया जाता है। (?<name>pattern) नामित कैप्चर(?:pattern) नॉन-कैप्चरिंग ग्रुप है, जो शुद्ध रूप से alternation ((?:cat|dog)) या क्वांटिफ़ायर को लपेटने के लिए है। ग्रुप के अंदर | विकल्प हैं।

अंत में फ़्लैगi केस-असंवेदनशील, m मल्टीलाइन, s dotall, u यूनिकोड।

तुलना और डेटा

क्वांटिफ़ायरGreedy (डिफ़ॉल्ट)Lazy (? सफ़िक्स)Possessive (जहाँ समर्थित)
* / *? / *+अधिकतम, फ़ेल पर पीछेन्यूनतम, माँग पर बढ़ेअधिकतम, कभी पीछे नहीं
+ / +? / ++एक या अधिक, greedyएक या अधिक, lazyएक या अधिक, no-backtrack
? / ?? / ?+0–1, 1 प्राथमिक0–1, 0 प्राथमिक0–1, no-backtrack
{n,m} / {n,m}? / {n,m}+रेंज, greedyरेंज, lazyरेंज, no-backtrack

Greedy डिफ़ॉल्ट इसलिए है क्योंकि आमतौर पर वही सही होता है। Lazy चाहिए जब पीछे वाला पैटर्न बहुत उदार हो। HTML स्निपेट से <b>...</b> जोड़ी निकालने में <b>(.*?)</b> lazy इस्तेमाल करें ताकि वह कई टैग न निगले (और फिर भी असली HTML पर रेगेक्स न लगाएँ)।

वास्तविक परिदृश्य

परिदृश्य 1 — व्यावहारिक ईमेल मैच। RFC 5322 का पूर्ण ईमेल grammar टिप्पणियाँ, quoted local parts, IP literal नेस्टिंग तक अनुमति देता है; इसे पूरी तरह पकड़ने वाला रेगेक्स बदनाम विशाल है (सबसे प्रसिद्ध प्रयास 6,425 अक्षर), और फिर भी वह असली पार्सर नहीं। मैं प्रोडक्शन में लगभग हमेशा ^[^\s@]+@[^\s@]+\.[^\s@]+$ इस्तेमाल करता हूँ — “ख़ाली नहीं, spaces नहीं, @ ग़लत जगह नहीं, डोमेन में कम से कम एक डॉट”। आकार रेगेक्स से, अस्तित्व एक पुष्टिकरण ईमेल से।

परिदृश्य 2 — अंतरराष्ट्रीय फ़ॉर्मैट वाले फ़ोन नंबर। +91 11 2345 6789, (011) 2345-6789, 91-11-2345-6789 एक ही नंबर के अलग-अलग रूप हैं। ^\+?\d{1,3}[-\s().]*\d{1,4}[-\s().]*\d{3,4}[-\s().]*\d{3,4}$ जैसा पैटर्न सामान्य विराम चिह्न स्वीकार करता है, और बाद में सामान्यीकरण चरण उन्हें हटाकर केवल अंक रखता है। राउटिंग, स्टोरेज, डायल जैसे ज़रूरी प्रसंस्करण के लिए Google libphonenumber का उपयोग करें।

परिदृश्य 3 — एक लॉग पंक्ति से फ़ील्ड निकालना। 2026-04-13T02:11:05Z 192.0.2.42 "GET /search?q=foo HTTP/1.1" 200 1534 जैसी पंक्ति को एक पैटर्न से तोड़ा जा सकता है: ^(?<ts>\S+)\s+(?<ip>\S+)\s+"(?<method>\w+)\s+(?<path>\S+)\s+\S+"\s+(?<status>\d+)\s+(?<bytes>\d+)$। यहाँ नामित ग्रुप की उपयोगिता साफ़ होती है; मैच ऑब्जेक्ट डिक्शनरी की तरह पहुँच देता है, और हर फ़ील्ड नाम से निकलता है।

आम ग़लतफ़हमियाँ

“रेगेक्स से ईमेल पूरी तरह वैलिडेट हो सकता है।” केवल आकार। RFC 5322 रेगेक्स में उचित रूप से एन्कोड करने लायक़ नहीं है, और एन्कोड कर भी लें तो “पता आकार में सही है” का मतलब “मेलबॉक्स मौजूद है” नहीं।

“Greedy हमेशा lazy से धीमा होता है।” सच नहीं। अगर सब-पैटर्न बहुत सीमित है तो greedy एक लंबी छलाँग में ख़त्म होकर तेज़ हो सकता है। Lazy वहाँ जीतता है जब पीछे वाला पैटर्न मैच को एंकर करता है।

“रेगेक्स इंजन एक जैसे हैं।” नहीं। ECMAScript में possessive क्वांटिफ़ायर और atomic ग्रुप नहीं, Python re की अपनी यूनिकोड प्रॉपर्टी है, PCRE बैकरेफ़्रेंस और recursion तक देता है। Go RE2 backreference और लुकअराउंड छोड़ देता है पर इनपुट लंबाई पर linear समय देता है।

“रेगेक्स से HTML (या JSON, XML) पार्स हो सकता है।” नियमित भाषाएँ संतुलित नेस्टिंग नहीं वर्णित कर सकतीं। विशेष attribute value जैसा अच्छी तरह परिभाषित सब-पैटर्न निकालने तक रेगेक्स ठीक है, पर पूरा पेड़ सही ढंग से नहीं। नेस्टेड फ़ॉर्मैट के लिए पार्सर (DOMParser, JSON.parse, XML लाइब्रेरी, CSV रीडर) का उपयोग करें।

चेकलिस्ट

  1. इनपुट क्या है और counter-examples क्या हैं? दोनों पैटर्न लिखने से पहले लिख लें।
  2. क्या डेटा नेस्टेड·रिकर्सिव है? तो पार्सर इस्तेमाल करें। रेगेक्स ग़लत टूल है।
  3. किस इंजन को टारगेट कर रहे हैं? JS·Python·Go·PCRE में अंतर हैं।
  4. क्या आपको असली मैच चाहिए या बस हाँ/नहीं? क्वांटिफ़ायर/alternation के लिए ग्रुप को (?:...) नॉन-कैप्चरिंग रखें।
  5. क्या पैटर्न यूज़र इनपुट पर या अविश्वसनीय इनपुट पर लागू होगा? atomic group, समय सीमा, या RE2 जैसे linear-time इंजन से catastrophic backtracking रोकें।
  6. क्या बाद में टेक्स्ट नॉर्मलाइज़ेशन होगा? सब एक पैटर्न में न ठूँसें।
  7. क्या पैटर्न दस्तावेज़ित है? x (extended) मोड की टिप्पणी या ऊपर एक पंक्ति का विवरण अगले व्यक्ति के लिए बड़ी बीमा है।

संबंधित टूल

Patrache Studio का रेगेक्स टेस्टर नमूना इनपुट पर पैटर्न चलाकर कैप्चर इनलाइन दिखाता है। अगर मैच का लक्ष्य लॉग में JSON जैसा संरचित टेक्स्ट है, तो JSON फ़ॉर्मैटिंग·वैलिडेशन·JSON Schema के साथ जोड़कर निकाले गए टुकड़ों को दूसरी रेगेक्स से नहीं, उचित पार्सर से वैलिडेट करें। रेगेक्स का एक आम लक्ष्य URL में बैठा UUID है — UUID v1·v4·v7 तुलना में वही 36-अक्षर स्ट्रिंग वर्ज़न बिट के आधार पर भिन्न अर्थ क्यों रखती है, यह देख सकते हैं।

संदर्भ