एक वाक्य में
Regular expressions (या "regex") कैरेक्टर्स का एक खास सीक्वेंस है जो एक सर्च पैटर्न डिफाइन करता है, जिससे आप टेक्स्ट को सर्जिकल सटीकता (surgical precision) के साथ ढूंढ, बदल और वैलिडेट कर सकते हैं।
यह क्या समस्या हल करता है
ज़रा सोचिए: आपके पास एक बहुत बड़ी लॉग फ़ाइल है, और आपको पिछले घंटे में एक खास IP एड्रेस से आए हर एरर मैसेज को खोजना है। "error" के लिए एक साधारण टेक्स्ट सर्च करना बेकार जानकारी की बाढ़ जैसा है। आप if स्टेटमेंट्स और स्ट्रिंग-स्प्लिटिंग लॉजिक का एक झुंड लिखकर एक स्क्रिप्ट बना सकते हैं, लेकिन वह आसानी से टूट जाती है, लिखने में धीमी होती है, और डीबग करने में सिरदर्द बन जाती है।
यह वह शुरुआती दौर की देन है जहां से regex का जन्म हुआ। पुराने दिनों में, केन थॉम्पसन जैसे Unix के दिग्गजों को टेक्स्ट के साथ काम करने के लिए एक बेहतर तरीके की ज़रूरत थी। वे grep (Global Regular Expression Print) और टेक्स्ट एडिटर ed जैसे टूल बना रहे थे। एक साधारण Ctrl+F से काम नहीं चलने वाला था। उन्हें सिर्फ लिटरल टेक्स्ट खोजने के बजाय, जिस टेक्स्ट को वे ढूंढ रहे थे उसे वर्णन करने के लिए एक भाषा की ज़रूरत थी।
regex जो समस्या हल करता है, वह है imperative "इसे-कैसे-ढूंढें" वाले तरीके (लाइनों के माध्यम से लूप करें, जांचें कि क्या एक लाइन में यह है, फिर जांचें कि क्या उसमें वह भी है...) से हटकर declarative "यह-कैसा-दिखता-है" वाले तरीके की ओर ले जाना। आप कंप्यूटर को एक छोटा, कॉम्पैक्ट पैटर्न देते हैं, और वह उस विवरण से मेल खाने वाले सभी टेक्स्ट को खोजने का भारी काम करता है। यह वैसा ही है जैसे किसी को कदम-दर-कदम रास्ता बताने के बजाय सीधे मंजिल की तस्वीर दिखा देना।
यह अंदर से कैसे काम करता है
एक रेगुलर एक्सप्रेशन प्रतीकों (symbols) के एक उलझे हुए जंजाल जैसा दिख सकता है, लेकिन असल में यह एक बहुत ही संरचित मिनी-प्रोग्राम है। एक खास सॉफ्टवेयर जिसे "regex engine" कहते हैं, आपके पैटर्न को पढ़ता है और इसका इस्तेमाल इनपुट टेक्स्ट को स्कैन करने के लिए करता है। आइए इस जादुई मंत्र को तोड़कर समझते हैं।
बिल्डिंग ब्लॉक्स: Literals और Metacharacters
अपने मूल में, एक regex पैटर्न दो तरह के कैरेक्टर्स से बना होता है:
- Literals: ये सिर्फ सामान्य कैरेक्टर होते हैं जो खुद से मैच करते हैं। पैटर्न
cat"c", "a", और "t" अक्षरों के सटीक क्रम को ढूंढेगा। बिलकुल आसान। - Metacharacters: ये खास मसाला हैं। ये खुद से मैच नहीं करते; इनके पास एक सुपरपावर होती है। डॉट (
.) इसका एक क्लासिक उदाहरण है। यह एक वाइल्डकार्ड है जो किसी भी एक कैरेक्टर से मैच करता है (आमतौर पर, न्यूलाइन को छोड़कर)। तो,c.t"cat", "cot", "c_t", और यहाँ तक कि "c!t" से भी मैच करेगा।
दूसरे बड़े खिलाड़ियों में * (पिछले वाले को 0 या अधिक बार मैच करें), + (1 या अधिक बार), और ? (0 या 1 बार) शामिल हैं। इन्हें quantifiers कहते हैं।
कैरेक्टर क्लासेस और शॉर्टहैंड्स
क्या होगा अगर आप किसी भी स्वर (vowel) से मैच करना चाहते हैं? आप (a|e|i|o|u) लिख सकते हैं, लेकिन यह अटपटा है। इसके बजाय, आप एक कैरेक्टर क्लास का उपयोग कर सकते हैं: [aeiou]। स्क्वायर ब्रैकेट आपको अपने अनुमत कैरेक्टर्स का सेट परिभाषित करने देते हैं।
यह रेंज के साथ और भी बेहतर हो जाता है। किसी भी लोअरकेस अक्षर से मैच करना चाहते हैं? [a-z]। कोई भी संख्या? [0-9]।
आपकी टाइपिंग और बचाने के लिए, regex में आम क्लासेस के लिए शॉर्टहैंड हैं:
\d: कोई भी अंक ([0-9])\w: कोई भी "वर्ड" कैरेक्टर (अक्षर, संख्या, और अंडरस्कोर) ([a-zA-Z0-9_])\s: कोई भी व्हाइटस्पेस कैरेक्टर (स्पेस, टैब, न्यूलाइन)\D,\W,\S: इनके विपरीत! जो कुछ भी अंक, वर्ड कैरेक्टर या व्हाइटस्पेस नहीं है, उससे मैच करें।
Quantifiers: कितनी बार?
हम पहले *, +, और ? से मिल चुके हैं। वे इंजन को बताते हैं कि पिछले कैरेक्टर या ग्रुप को कितनी बार मैच करना है।
| Quantifier | मतलब | उदाहरण | मैच करता है |
|---|---|---|---|
? |
शून्य या एक बार | colou?r |
"color", "colour" |
* |
शून्य या अधिक बार | goa*l |
"gl", "gol", "goooal" |
+ |
एक या अधिक बार | goa+l |
"goal", "goooal" |
{n} |
ठीक n बार | \d{4} |
"1984" |
{n,} |
n या अधिक बार | \w{3,} |
"cat", "tiger" |
{n,m} |
n और m के बीच में बार | [a-z]{5,7} |
"regex", "pattern" |
एक महत्वपूर्ण विवरण यह है कि ये क्वांटिफायर डिफ़ॉल्ट रूप से "greedy" (लालची) होते हैं। वे जितना संभव हो उतना टेक्स्ट मैच करने की कोशिश करेंगे। यदि आपके पास टेक्स्ट <p>first</p><p>second</p> है और पैटर्न /<p>.*</p>/ है, तो greedy .* पहले <p> से लेकर आखिरी </p> तक सब कुछ मैच कर लेगा। इसे "lazy" (आलसी) बनाने के लिए (सबसे छोटी संभव स्ट्रिंग से मैच करने के लिए), आप एक ? जोड़ते हैं: /<p>.*?</p>/। अब यह प्रत्येक <p>...</p> टैग को व्यक्तिगत रूप से मैच करेगा।
Anchors और Boundaries
Anchors कैरेक्टर मैच नहीं करते; वे पोजीशन मैच करते हैं।
^: स्ट्रिंग की शुरुआत में पोजीशन की पुष्टि करता है (या मल्टीलाइन मोड में लाइन की शुरुआत में)।^catकेवल "cat" से मैच करेगा अगर वह बिल्कुल शुरुआत में है।$: स्ट्रिंग के अंत में पोजीशन की पुष्टि करता है (या लाइन के अंत में)।cat$केवल "cat" से मैच करेगा अगर वह बिल्कुल अंत में है।\b: एक "वर्ड बाउंड्री" की पुष्टि करता है—एक वर्ड कैरेक्टर (\w) और एक नॉन-वर्ड कैरेक्टर (\W) के बीच की जगह। पैटर्न\bcat\b"the cat sat" में "cat" से मैच करेगा, लेकिन "concatenate" में नहीं। यह पूरे शब्दों को मैच करने के लिए अविश्वसनीय रूप से उपयोगी है।
ग्रुपिंग और कैप्चरिंग
पैरेंथेसिस () दो काम करते हैं:
- ग्रुप: वे पैटर्न के एक हिस्से को ग्रुप करते हैं ताकि आप उस पर एक क्वांटिफायर लगा सकें।
(ha)+"ha", "haha", "hahaha", आदि से मैच करेगा। - कैप्चर: वे उस टेक्स्ट को "कैप्चर" करते हैं जो उनके अंदर मैच हुआ है। यह एक सुपरपावर है। यदि आप टेक्स्ट "ID: 12345" को पैटर्न
ID: (\d+)से मैच करते हैं, तो इंजन न केवल आपको बताता है कि उसे एक मैच मिला है, बल्कि वह आपको कैप्चर की गई स्ट्रिंग "12345" भी देता है। आप इन कैप्चर किए गए ग्रुप्स (अक्सर$1,$2, आदि या\1,\2कहा जाता है) का उपयोग रिप्लेसमेंट ऑपरेशन में कर सकते हैं या उन्हें प्रोसेसिंग के लिए निकाल सकते हैं।
Regex इंजन: NFA vs. DFA
यह थोड़ी गहरी बात है, लेकिन यह समझाता है कि कुछ regex विनाशकारी रूप से धीमे क्यों हो सकते हैं। अधिकांश इंजन जो आप उपयोग करते हैं (JavaScript, Python, Perl, Java में) "Nondeterministic Finite Automaton" (NFA) पर आधारित होते हैं। वे पैटर्न के माध्यम से सभी संभावित रास्तों को आज़माकर काम करते हैं। यह शक्तिशाली है क्योंकि यह "बैक-रेफरेंस" (उसी टेक्स्ट से मेल खाना जो पहले किसी ग्रुप द्वारा कैप्चर किया गया था) जैसी उन्नत सुविधाओं की अनुमति देता है। हालाँकि, यह "catastrophic backtracking" नामक समस्या का कारण भी बन सकता है, जिसमें स्टेप्स की संख्या तेजी से बढ़ती है, जहां एक मुश्किल स्ट्रिंग पर एक खराब लिखा गया पैटर्न आपकी ऐप को हैंग कर सकता है।
पुराने टूल (और कुछ आधुनिक विशेष टूल जैसे Google का RE2) "Deterministic Finite Automaton" (DFA) का उपयोग करते हैं। DFA बहुत तेज़ होते हैं और बैकट्रैकिंग लूप में नहीं फंस सकते, लेकिन वे कम एक्सप्रेसिव होते हैं और NFA की सभी फैंसी सुविधाओं का समर्थन नहीं करते हैं।
वास्तविक दुनिया की कहानियाँ
लॉग फ़ाइल का जासूस
एक वेब सर्वर ने रैंडम 500 एरर देना शुरू कर दिया, और DevOps टीम हाथ-पांव मार रही थी। लॉग फाइलें गीगाबाइट्स के रूटीन एक्सेस लॉग्स की बाढ़ थीं, जिसमें महत्वपूर्ण एरर मैसेज मिले हुए थे। मैन्युअल रूप से grep करने से उन्हें कुछ खास नहीं मिल रहा था। एक जूनियर डेवलपर ने, अपनी कंप्यूटर साइंस क्लास को याद करते हुए, फटाफट एक regex बनाया: ^\[.*?\] \[error\].*?client: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})। यह पैटर्न सीधे उन लाइनों पर पहुंचा जो टाइमस्टैम्प ^\[.*?\] से शुरू होती हैं, जिनमें [error] होता है, और फिर क्लाइंट IP एड्रेस को कैप्चर कर लेता है। कुछ ही सेकंड में, उनके पास मुट्ठी भर IP एड्रेस की एक सूची थी जो सभी एरर को ट्रिगर कर रहे थे। पता चला कि यह एक खराब वेब स्क्रैपर था जो एक विशेष API एंडपॉइंट पर जोर-जोर से हिट कर रहा था।
सबक: Regex टेक्स्टुअल डेटा के भूसे के ढेर में तुरंत सुई ढूंढ सकता है, एक भारी समस्या को एक लक्षित जांच में बदल देता है।
महान रिफैक्टरिंग का बचाव
एक स्टार्टअप ने अपने कोडबेस में एक मुख्य कॉन्सेप्ट को रीब्रांड करने का फैसला किया। फंक्शन create_legacy_widget() का नाम हर जगह build_standard_component() करना था। एक साधारण फाइंड-एंड-रिप्लेस तबाही का नुस्खा था—यह अलग-अलग स्पेसिंग वाले मामलों को छोड़ देता, और इससे भी बदतर, यह गलती से कमेंट्स या डॉक्यूमेंटेशन स्ट्रिंग्स के अंदर की चीजों को बदल सकता था। एक डेवलपर ने अपने एडिटर के regex फाइंड-एंड-रिप्लेस का इस्तेमाल किया। उन्होंने create_legacy_widget\s*\(\s*(\w+)\s*\) खोजा और इसे build_standard_component($1) से बदल दिया। पैटर्न ने चालाकी से वैकल्पिक व्हाइटस्पेस (\s*) को संभाला और फंक्शन को पास किए गए आर्गुमेंट ((\w+)) को कैप्चर किया, इसे रिप्लेसमेंट में फिर से ($1) डाल दिया। यह पूरा, विशाल रिफैक्टरिंग एक मिनट से भी कम समय में सुरक्षित रूप से हो गया।
सबक: Regex सर्जिकल, संदर्भ-जागरूक कोड संशोधनों को सक्षम बनाता है जो बुनियादी खोज उपकरणों के साथ असंभव हैं।
फॉर्म्स का द्वारपाल
एक डेवलपर एक नया यूजर साइन-अप फॉर्म बना रहा था। प्रोडक्ट मैनेजर के यूजरनेम के लिए खास नियम थे: "3 से 15 कैरेक्टर, केवल अक्षर, संख्या और अंडरस्कोर।" पहली कोशिश if स्टेटमेंट्स की एक श्रृंखला थी: लंबाई की जांच करें, फिर प्रत्येक कैरेक्टर की जांच के लिए स्ट्रिंग के माध्यम से लूप करें। यह भद्दा और अक्षम था। एक अन्य डेवलपर ने आकर पूरे कोड ब्लॉक को एक ही लाइन से बदल दिया: if ( /^[a-zA-Z0-9_]{3,15}$/.test(username) )। पैटर्न ^...$ ने मैच को पूरी स्ट्रिंग पर एंकर कर दिया, यह सुनिश्चित करते हुए कि कोई भी आवारा कैरेक्टर की अनुमति नहीं है, और [a-zA-Z0-9_]{3,15} ने कैरेक्टर सेट और लंबाई के नियमों को एक ही बार में लागू कर दिया।
सबक: डेटा वैलिडेशन के लिए, regex फॉर्मेटिंग नियमों को परिभाषित करने और लागू करने का सबसे संक्षिप्त और शक्तिशाली तरीका है।
आम गलतियाँ और जाल
- लालच हमेशा अच्छा नहीं होता। याद रखें कि
*और+जैसे क्वांटिफायर greedy होते हैं। यदि आप "Make it<b>bold</b>and<b>strong</b>" टेक्स्ट पर<b>.*</b>के साथ HTML टैग्स को मैच करने की कोशिश कर रहे हैं, तो आप पहले<b>से लेकर आखिरी</b>तक पूरी स्ट्रिंग को मैच कर लेंगे। सबसे छोटे संभव टेक्स्ट को मैच करने के लिए lazy क्वांटिफायर*?का उपयोग करें:<b>.*?</b>। - स्पेशल कैरेक्टर्स को एस्केप करना भूल जाना। यदि आप एक लिटरल डॉट
.या प्लस साइन+से मैच करना चाहते हैं, तो आपको इसे बैकस्लैश से एस्केप करना होगा:\.,\+। पैटर्न1+1के साथ1+1खोजना विफल हो जाएगा, क्योंकि+एक क्वांटिफायर है। आपको1\+1चाहिए। - 'डॉट-मैचेस-ऑल' वाला धोखा।
.मेटाकैरेक्टर एक शक्तिशाली वाइल्डकार्ड है, लेकिन डिफ़ॉल्ट रूप से यह न्यूलाइन कैरेक्टर से मैच नहीं करता है। मल्टी-लाइन टेक्स्ट को पार्स करते समय यह आपको फंसा सकता है। अधिकांश regex इंजनों में "dotall" या "single line" मोड होता है (अक्सरsजैसे फ्लैग द्वारा सक्रिय होता है) जो.को न्यूलाइन से भी मैच कराता है। - Catastrophic Backtracking. एक regex जैसे
(a+)+bसरल लगता है, लेकिन जब इसे "aaaaaaaaaaaaaaaaaaaaaaaaaaac" जैसी स्ट्रिंग पर चलाया जाता है, तो NFA इंजनa's को ग्रुप करने के अनगिनत तरीकों में खो सकता है। यह आपके प्रोग्राम को फ्रीज कर सकता है। नेस्टेड क्वांटिफायर से सावधान रहें, खासकर जब भीतरी ग्रुप एक ही टेक्स्ट को कई तरीकों से मैच कर सकता है। - मल्टीलाइन मोड में एंकर को मिक्स कर देना। जब आप मल्टीलाइन मोड (
mफ्लैग) को सक्षम करते हैं, तो^और$का अर्थ बदल जाता है। वे अब पूरी स्ट्रिंग के पूर्ण शुरुआत/अंत से मेल नहीं खाते, बल्कि किसी भी लाइन के शुरुआत/अंत से मेल खाते हैं। इसे भूलने से आश्चर्यजनक मैच या नॉन-मैच हो सकते हैं।
यह आपकी नज़र में क्यों होना चाहिए
आपको regex के बारे में तब सोचना चाहिए जब आप टेक्स्ट से जुड़ी ऐसी समस्या का सामना करते हैं जिसकी एक अनुमानित संरचना (structure) हो। यह टेक्स्ट का अर्थ समझने का टूल नहीं है, बल्कि उसके पैटर्न को समझने का है। इसे इन कामों के लिए अपनी जेब में रखें:
- वैलिडेशन: क्या यह एक वैध ईमेल एड्रेस है? एक वैध फोन नंबर? एक वैध हेक्स कलर कोड? एक वैध URL? Regex आपके डेटा के लिए दरवाज़े पर खड़ा बाउंसर है।
- पार्सिंग: गंदे, असंरचित टेक्स्ट से संरचित डेटा निकालना। वेबसाइट स्क्रैप करने, सर्वर लॉग का विश्लेषण करने, या रिपोर्ट प्रोसेस करने के बारे में सोचें।
- कोड ट्रांसफॉर्मेशन: अपने कोडबेस (codemods) या कॉन्फ़िगरेशन फ़ाइलों में जटिल फाइंड-एंड-रिप्लेस ऑपरेशन करना।
- राउटिंग और रीराइटिंग: Nginx और Apache जैसे वेब सर्वर URLs को फिर से लिखने और आने वाले अनुरोधों को आपके एप्लिकेशन के सही हिस्से में भेजने के लिए regex का भारी उपयोग करते हैं।
Regex सीखना एक डेवलपर सुपरपावर है। यह एक क्रॉस-प्लेटफॉर्म, लैंग्वेज-एग्नोस्टिक स्किल है जो आपके पूरे करियर में आपको फायदा देगी।
और गहराई में जाएं
- MDN Web Docs: Regular expressions - JavaScript regex के लिए एक निश्चित गाइड, लेकिन इसके कॉन्सेप्ट लगभग हर जगह लागू होते हैं।
- Wikipedia: Regular expression - कंप्यूटर विज्ञान के सिद्धांत और इतिहास में एक गहरी डुबकी।
- Regular-Expressions.info - एक अविश्वसनीय रूप से विस्तृत और व्यापक ट्यूटोरियल और रेफरेंस साइट।
- Google RE2 Syntax - एक लोकप्रिय, परफॉरमेंस-केंद्रित DFA-आधारित regex इंजन पर एक दिलचस्प नज़र।
- PCRE Man Pages - Perl Compatible Regular Expressions का मैनुअल, वह सिंटैक्स जिसने कई आधुनिक regex फ्लेवर्स को प्रेरित किया है।