Python रेगुलर एक्सप्रेशन

रेगुलर एक्सप्रेशन, टेक्स्ट पैटर्न को मैच करने वाली बेहतरीन भाषा का इस्तेमाल करते हैं. यह पेज हमारे Python एक्सरसाइज़ के लिए ज़रूरी रेगुलर एक्सप्रेशन के बारे में बुनियादी जानकारी देता है. साथ ही, इसमें यह भी दिखाया गया है कि Python में रेगुलर एक्सप्रेशन कैसे काम करते हैं. Python "re" मॉड्यूल रेगुलर एक्सप्रेशन का समर्थन करता है. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है

Python में, रेगुलर एक्सप्रेशन खोज आम तौर पर इस तरह लिखी जाती है:

match = re.search(pat, str)

re.search() वाला तरीका, रेगुलर एक्सप्रेशन पैटर्न और स्ट्रिंग का इस्तेमाल करके स्ट्रिंग में उस पैटर्न को खोजता है. अगर खोज पूरी होती है, तो search() मैच ऑब्जेक्ट दिखाता है या कोई नहीं दिखाता है. इसलिए, खोज के सफल होने की जाँच करने के लिए, आम तौर पर खोज के तुरंत बाद एक if-स्टेटमेंट आता है, जैसा कि नीचे दिए गए उदाहरण में दिखाया गया है जो 'word:' पैटर्न की खोज करता है: उसके बाद 3 अक्षरों वाला शब्द (विवरण नीचे दिया गया है):

import re

str = 'an example word:cat!!'
match = re.search(r'word:\w\w\w', str)
# If-statement after search() tests if it succeeded
if match:
  print('found', match.group()) ## 'found word:cat'
else:
  print('did not find')

कोड match = re.search(pat, str), खोज के नतीजे को "match" वैरिएबल में सेव करता है. इसके बाद, if-स्टेटमेंट मैच की जांच करता है -- अगर सही है और मैच.group(), मेल खाने वाला टेक्स्ट है (उदाहरण के लिए, 'word:cat'). नहीं तो, अगर मैच गलत है (ज़्यादा सटीक नहीं है), तो इसका मतलब है कि खोज नहीं हो पाई और कोई भी मिलता-जुलता टेक्स्ट नहीं मिला.

'r' पैटर्न स्ट्रिंग के शुरू में Python "raw" लिखा होता है ऐसी स्ट्रिंग जो बिना बदलाव के बैकस्लैश से गुजरती है, जो रेगुलर एक्सप्रेशन के लिए बहुत आसान है (Java को इस सुविधा की बुरी ज़रूरत है!). मेरा सुझाव है कि आप हमेशा 'r' के साथ पैटर्न स्ट्रिंग लिखें नियमित रूप से करते हैं.

बुनियादी पैटर्न

रेगुलर एक्सप्रेशन की शक्ति यह है कि वे सिर्फ़ तय वर्णों के साथ-साथ पैटर्न भी तय कर सकते हैं. किसी एक वर्ण से मेल खाने वाले सबसे बुनियादी पैटर्न यहां दिए गए हैं:

  • अंग्रेज़ी के छोटे अक्षर "a" के ऊपर बना खास निशान, X, 9, < -- साधारण वर्ण इनसे पूरी तरह मेल खाते हैं. वे मेटा-वर्ण जो एक-दूसरे से मेल नहीं खाते, क्योंकि उनके खास मतलब होते हैं: . ^ $ * + ? { [ ] \ | ( ) (विवरण नीचे दिया गया है)
  • . (एक विराम चिह्न) -- नई पंक्ति '\n' को छोड़कर किसी भी एक वर्ण से मेल खाता है
  • \w -- (लोअरकेस w) "शब्द" से मेल खाता है वर्ण: कोई अक्षर या अंक या अंडरबार [a-zA-Z0-9_]. ध्यान दें कि भले ही "शब्द" यह याद रखने के लिए है, यह सिर्फ़ एक शब्द से मेल खाता है, पूरे शब्द से नहीं. \W (अपर केस W) बिना शब्द वाले किसी भी वर्ण से मैच करता है.
  • \b -- शब्द और गैर-शब्द के बीच सीमा
  • \s -- (लोअरकेस s) एक खाली सफ़ेद जगह से मैच करता है -- space, newline, Return, tab, form [ \n\r\t\f]. \S (ऊपरी केस S) किसी भी ऐसे वर्ण से मेल खाता है जो खाली सफ़ेद जगह नहीं है.
  • \t, \n, \r -- टैब, न्यूलाइन, रिटर्न
  • \d -- दशमलव अंक [0-9] (कुछ पुरानी रेगुलर एक्सप्रेशन सुविधाएं \d के साथ काम नहीं करतीं, लेकिन वे सभी \w और \s के साथ काम करती हैं)
  • ^ = start, $ = end -- स्ट्रिंग के प्रारंभ या अंत का मिलान करें
  • \ -- "विशेषज्ञता" रोकें एक वर्ण होना चाहिए. इसलिए, उदाहरण के लिए, \ का इस्तेमाल करें. पीरियड या \\ का मिलान करने के लिए. अगर आपको पक्के तौर पर नहीं पता कि किसी वर्ण का कोई खास मतलब है या नहीं, जैसे कि '@', तो उसके आगे \@ का इस्तेमाल करके स्लैश का इस्तेमाल किया जा सकता है. अगर यह \c जैसा मान्य एस्केप सीक्वेंस नहीं है, तो आपका Python प्रोग्राम किसी गड़बड़ी के साथ रुक जाएगा.

बुनियादी उदाहरण

मज़ाक़: तीन आंखों वाले सूअर को क्या कहते हैं? पिग!

किसी स्ट्रिंग में पैटर्न के लिए रेगुलर एक्सप्रेशन खोज के बुनियादी नियम ये हैं:

  • स्ट्रिंग में शुरू से आखिर तक खोज होती है और पहले मिलान पर रुकती है
  • सभी पैटर्न का मेल खाना चाहिए, लेकिन सभी स्ट्रिंग का नहीं
  • अगर match = re.search(pat, str) सही है, तो 'कोई नहीं' नहीं है और खास तौर पर, Match.group() मेल खाने वाला टेक्स्ट है
  ## Search for pattern 'iii' in string 'piiig'.
  ## All of the pattern must match, but it may appear anywhere.
  ## On success, match.group() is matched text.
  match = re.search(r'iii', 'piiig') # found, match.group() == "iii"
  match = re.search(r'igs', 'piiig') # not found, match == None

  ## . = any char but \n
  match = re.search(r'..g', 'piiig') # found, match.group() == "iig"

  ## \d = digit char, \w = word char
  match = re.search(r'\d\d\d', 'p123g') # found, match.group() == "123"
  match = re.search(r'\w\w\w', '@@abcd!!') # found, match.group() == "abc"

दोहराव

पैटर्न में दोहराव की जानकारी देने के लिए, + और * का इस्तेमाल करने पर चीज़ें और दिलचस्प हो जाती हैं

  • + -- पैटर्न की बाईं ओर एक या उससे ज़्यादा बार, जैसे कि 'आई+' = एक या अधिक i's
  • * -- पैटर्न की बाईं ओर 0 या इससे ज़्यादा बार
  • ? -- इसकी बाईं ओर के पैटर्न की 0 या 1 घटनाओं का मिलान करें

सबसे बाएं और सबसे बड़ा

सबसे पहले खोज, पैटर्न के लिए सबसे बाईं ओर का मिलान ढूंढती है और दूसरी वह स्ट्रिंग का जितना संभव हो सके उतना उपयोग करने का प्रयास करती है -- यानी + और * जितना संभव हो उतना दूर जाएं (+ और * को "लालची" कहा जाता है).

दोहराव के उदाहरण

  ## i+ = one or more i's, as many as possible.
  match = re.search(r'pi+', 'piiig') # found, match.group() == "piii"

  ## Finds the first/leftmost solution, and within it drives the +
  ## as far as possible (aka 'leftmost and largest').
  ## In this example, note that it does not get to the second set of i's.
  match = re.search(r'i+', 'piigiiii') # found, match.group() == "ii"

  ## \s* = zero or more whitespace chars
  ## Here look for 3 digits, possibly separated by whitespace.
  match = re.search(r'\d\s*\d\s*\d', 'xx1 2   3xx') # found, match.group() == "1 2   3"
  match = re.search(r'\d\s*\d\s*\d', 'xx12  3xx') # found, match.group() == "12  3"
  match = re.search(r'\d\s*\d\s*\d', 'xx123xx') # found, match.group() == "123"

  ## ^ = matches the start of string, so this fails:
  match = re.search(r'^b\w+', 'foobar') # not found, match == None
  ## but without the ^ it succeeds:
  match = re.search(r'b\w+', 'foobar') # found, match.group() == "bar"

ईमेल का उदाहरण

मान लें कि आपको 'xyz alice-b@google.com बैंगनी बंदर' स्ट्रिंग में ईमेल पता खोजना है. हम रेगुलर एक्सप्रेशन की ज़्यादा सुविधाओं को दिखाने के लिए, इसे एक उदाहरण के तौर पर इस्तेमाल करेंगे. यहां r'\w+@\w+' पैटर्न का इस्तेमाल करने की कोशिश की गई है:

  str = 'purple alice-b@google.com monkey dishwasher'
  match = re.search(r'\w+@\w+', str)
  if match:
    print(match.group())  ## 'b@google'

इस मामले में खोज को पूरा ईमेल पता नहीं मिलता है, क्योंकि \w का मिलान '-' से नहीं होता है या '.' के पते में शामिल करें. हम नीचे दी गई रेगुलर एक्सप्रेशन सुविधाओं का इस्तेमाल करके इसे ठीक करेंगे.

स्क्वेयर ब्रैकेट

स्क्वेयर ब्रैकेट का इस्तेमाल, वर्णों के सेट के बारे में बताने के लिए किया जा सकता है, ताकि [abc] 'a' से मेल खाए या 'b' या 'c'. \w, \s वगैरह कोड स्क्वेयर ब्रैकेट में भी काम करते हैं. इनमें, बिंदु (.) का मतलब सिर्फ़ लिटरल वैल्यू है. ईमेल से जुड़ी समस्या के लिए, स्क्वेयर ब्रैकेट से '.' को आसानी से जोड़ा जा सकता है. और '-' का इस्तेमाल करके @ के आस-पास r'[\w.-]+@[\w.-]+' पैटर्न इस्तेमाल करके वर्णों के सेट का इस्तेमाल किया जा सकता है पूरा ईमेल पता पाने के लिए:

  match = re.search(r'[\w.-]+@[\w.-]+', str)
  if match:
    print(match.group())  ## 'alice-b@google.com'
(स्क्वेयर-ब्रैकेट की ज़्यादा सुविधाएं) रेंज को दिखाने के लिए, डैश का भी इस्तेमाल किया जा सकता है, ताकि [a-z] अंग्रेज़ी के सभी छोटे अक्षरों से मेल खाए. रेंज के बारे में बताए बिना डैश का इस्तेमाल करने के लिए, डैश को सबसे आखिर में रखें, उदाहरण के लिए [abc-]. स्क्वेयर-ब्रैकेट सेट की शुरुआत में एक अप-हैट (^) होता है, इसलिए [^ab] का मतलब 'a' को छोड़कर कोई भी वर्ण होता है या 'b'.

ग्रुप एक्सट्रैक्शन

"ग्रुप" रेगुलर एक्सप्रेशन की सुविधा की मदद से, मिलते-जुलते टेक्स्ट के हिस्सों को चुना जा सकता है. मान लीजिए कि ईमेल से जुड़ी किसी समस्या की वजह से, हम उपयोगकर्ता नाम और होस्ट को अलग-अलग एक्सट्रैक्ट करना चाहते हैं. ऐसा करने के लिए, उपयोगकर्ता नाम और पैटर्न में होस्ट के चारों ओर ब्रैकेट ( ) जोड़ें, जैसे कि: r'([\w.-]+)@)@([\w.-]+)'. इस मामले में, ब्रैकेट इस बात पर कोई असर नहीं डालते कि पैटर्न किस तरह मैच करेगा. इसके बजाय, वे लॉजिकल "ग्रुप" बनाते हैं डालें. खोजे गए नतीजे में, Match.group(1), पहले बाएं ब्रैकेट से मेल खाने वाला टेक्स्ट है और दूसरे बाएं ब्रैकेट से मैच करने वाला टेक्स्ट Match.group(2) है. सादा Match.group(), अब भी हमेशा की तरह पूरा मिलान वाला टेक्स्ट है.

  str = 'purple alice-b@google.com monkey dishwasher'
  match = re.search(r'([\w.-]+)@([\w.-]+)', str)
  if match:
    print(match.group())   ## 'alice-b@google.com' (the whole match)
    print(match.group(1))  ## 'alice-b' (the username, group 1)
    print(match.group(2))  ## 'google.com' (the host, group 2)

रेगुलर एक्सप्रेशन के साथ एक सामान्य वर्कफ़्लो यह है कि आप मनचाही चीज़ के लिए एक पैटर्न लिखते हैं, अपने मनचाहे भागों को निकालने के लिए कोष्ठकों के समूह जोड़कर.

Findall

Findall(), री मॉड्यूल में सबसे असरदार फ़ंक्शन हो सकता है. ऊपर हमने किसी पैटर्न का पहला मिलान ढूंढने के लिए re.search() का इस्तेमाल किया था. findall(), *सभी* मैच ढूंढता है और उन्हें स्ट्रिंग की सूची के तौर पर दिखाता है, जहां हर स्ट्रिंग एक मैच को दिखाती है.
  ## Suppose we have a text with many email addresses
  str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher'

  ## Here re.findall() returns a list of all the found email strings
  emails = re.findall(r'[\w\.-]+@[\w\.-]+', str) ## ['alice@google.com', 'bob@abc.com']
  for email in emails:
    # do something with each found email string
    print(email)

Files के साथ Findall

फ़ाइलों के लिए, आपको फ़ाइल की लाइनों को फिर से दोहराने की आदत हो सकती है और फिर हर लाइन पर findall() कॉल किया जा सकता है. इसके बजाय, findall() को अपने लिए बार-बार करने दें -- यह और भी बेहतर है! बस पूरी फ़ाइल के टेक्स्ट को findall() में फ़ीड करें और इससे एक ही चरण में सभी मैच की सूची दिखे. ध्यान रखें कि f.read(), एक ही स्ट्रिंग में फ़ाइल का पूरा टेक्स्ट दिखाता है:

  # Open file
  f = open('test.txt', encoding='utf-8')
  # Feed the file text into findall(); it returns a list of all the found strings
  strings = re.findall(r'some pattern', f.read())

Findall और Groups

ब्रैकेट ( ) ग्रुप के तरीके को findall() के साथ इस्तेमाल किया जा सकता है. अगर पैटर्न में दो या उससे ज़्यादा ब्रैकेट के ग्रुप हैं, तो स्ट्रिंग की सूची दिखाने के बजाय, findall(), *tuples* की सूची दिखाता है. हर ट्यूपल, पैटर्न का एक मैच दिखाता है और टपल के अंदर ग्रुप(1), ग्रुप(2) .. डेटा होता है. इसलिए, अगर ईमेल पैटर्न में दो ब्रैकेट ग्रुप जोड़े जाते हैं, तो findall(), ट्यूपल की सूची दिखाता है. हर लंबाई 2 में उपयोगकर्ता नाम और होस्ट शामिल होता है, जैसे कि ('एलिस', 'google.com').

  str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher'
  tuples = re.findall(r'([\w\.-]+)@([\w\.-]+)', str)
  print(tuples)  ## [('alice', 'google.com'), ('bob', 'abc.com')]
  for tuple in tuples:
    print(tuple[0])  ## username
    print(tuple[1])  ## host

जब आपके पास ट्यूपल की सूची हो, तो हर टपल की गिनती करने के लिए, उस पर लूप किया जा सकता है. अगर पैटर्न में कोई ब्रैकेट नहीं है, तो findall(), पहले के उदाहरणों की तरह, मिली स्ट्रिंग की सूची दिखाती है. अगर पैटर्न में ब्रैकेट का एक सेट होता है, तो findall() उस ग्रुप से जुड़ी स्ट्रिंग की सूची दिखाता है. (अस्पष्ट वैकल्पिक सुविधा: कभी-कभी आपके पैटर्न में कोष्ठक ( ) समूह होते हैं, लेकिन जिन्हें आप निकालना नहीं चाहते. ऐसे मामले में, ब्रैकेट की शुरुआत में ?: लिखें, जैसे कि (?: ) और उस बायां कोष्ठक को समूह परिणाम के रूप में नहीं गिना जाएगा.)

आरई का वर्कफ़्लो और डीबग

रेगुलर एक्सप्रेशन पैटर्न में, कुछ ही वर्णों में कई मतलब शामिल किए जा सकते हैं. हालांकि, इनमें बहुत ज़्यादा अंतर होता है. इसलिए, पैटर्न को डीबग करने में आपको काफ़ी समय लग सकता है. अपना रनटाइम सेट अप करें, ताकि आप पैटर्न को चला सकें और उससे मेल खाने वाली चीज़ को आसानी से प्रिंट कर सकें. उदाहरण के लिए, इसे एक छोटे टेस्ट टेक्स्ट पर चलाकर, findall() के नतीजे को प्रिंट करना. अगर पैटर्न किसी भी चीज़ से मेल नहीं खाता है, तो पैटर्न को कमज़ोर करने की कोशिश करें, उसके कुछ हिस्से हटा दें, ताकि आपको ज़्यादा मैच मिल सकें. देखने के लिए कुछ भी ठोस नहीं होता है, जब यह कुछ भी मैच नहीं करता, तो आप कोई प्रोग्रेस नहीं कर सकते. जब यह काफ़ी ज़्यादा मेल खा जाए, तो अपने हिसाब से लक्ष्यों को पूरा करने के लिए, इसे और कसें.

विकल्प

री फ़ंक्शन, पैटर्न के मैच के व्यवहार को बदलने के विकल्प चुनते हैं. विकल्प फ़्लैग को search() या findall() वगैरह में एक अतिरिक्त आर्ग्युमेंट के तौर पर जोड़ा जाता है, उदाहरण के लिए re.search(pat, str, re.IGNORECASE).

  • IGNORECASE -- मिलान के लिए अपर/लोअरकेस के अंतरों को अनदेखा करें, इसलिए 'a' दोनों 'a' से मेल खाता है और 'A'.
  • डॉटल -- बिंदु (.) को नई पंक्ति से मेल खाने की अनुमति दें -- आम तौर पर यह नई पंक्ति को छोड़कर किसी भी चीज़ से मेल खाता है. यह आपको उलझन में डाल सकता है -- आपको लगता है कि .* हर चीज़ से मेल खाता है, लेकिन डिफ़ॉल्ट रूप से यह किसी पंक्ति के अंत तक नहीं जाता. ध्यान दें कि \s (व्हाइटस्पेस) में नई लाइनें शामिल हैं. इसलिए, अगर आपको तय की गई खाली सफ़ेद जगह से मैच करना है, तो सिर्फ़ \s* का इस्तेमाल करें. इसमें एक नई लाइन भी हो सकती है
  • MULTILINE -- कई पंक्तियों से बनी स्ट्रिंग के अंदर, ^ और $ को हर लाइन के शुरुआती और आखिरी हिस्से का मिलान करने की अनुमति दें. आम तौर पर, ^/$ पूरी स्ट्रिंग के शुरुआती और आखिरी हिस्से से मेल खाएगा.

लालची बनाम लालच नहीं (ज़रूरी नहीं)

इस सेक्शन में रेगुलर एक्सप्रेशन की बेहतर तकनीक दिखाई जाती है. इसकी ज़रूरत नहीं होती.

मान लें कि आपके पास इसमें टैग वाला टेक्स्ट है: <b>foo</b> और <i>ऐसे ही चालू रहेगा</i>

मान लें कि हर टैग को '(<.*>)' पैटर्न से मैच करना है -- यह सबसे पहले क्या मेल खाता है?

नतीजा थोड़ा हैरान करने वाला है, लेकिन .* के लालची पहलू की वजह से यह पूरी '<b>foo</b> से मेल खाता है और <i>इसलिए</i>' एक बड़े मैच के रूप में. समस्या यह है कि * पहले > (इसे "लालची" भी कहते हैं).

रेगुलर एक्सप्रेशन का एक एक्सटेंशन है, जहां एक ? के आखिर में, जैसे कि .*? या .+? के ज़रिए उन्हें गैर-लालची में बदलना. अब वे जितनी जल्दी हो सके उतनी जल्दी बंद हो जाते हैं. इसलिए, '(<.*?>)' पैटर्न बस '<b>' के रूप में, और '</b>' का पता लगाने के लिए प्रोत्साहित करते हैं और इसी तरह प्रत्येक <..> उन्हें आपस में जोड़े. आम तौर पर, इस स्टाइल में .*? उसके ठीक बाद एक कंक्रीट मार्कर (> इस मामले में) से लेकर .*? चलाने के लिए बाध्य किया गया है.

*? एक्सटेंशन की शुरुआत Perl में हुई और पेर्ल के एक्सटेंशन वाले रेगुलर एक्सप्रेशन को Perl के संगत रेगुलर एक्सप्रेशन -- pcre कहा जाता है. Python में pcre support शामिल है. कई कमांड लाइन utils वगैरह में एक फ़्लैग होता है, जहां वे pcre पैटर्न स्वीकार करते हैं.

"X पर रुकने को छोड़कर इन सभी वर्णों" के आइडिया को कोड करने के लिए इस्तेमाल की जाने वाली यह पुरानी, लेकिन काफ़ी हद तक इस्तेमाल की जाने वाली तकनीक है स्क्वेयर-ब्रैकेट स्टाइल का इस्तेमाल करता है. ऊपर दिए गए पैटर्न के लिए, पैटर्न लिखा जा सकता है. हालांकि, सभी वर्ण पाने के लिए .* के बजाय, [^>]* का इस्तेमाल करें. इससे उन सभी वर्णों का इस्तेमाल नहीं किया जा सकता जो > नहीं हैं (पहले वाला ^ स्क्वेयर ब्रैकेट को "इनवर्ट" कर देता है, ताकि यह ऐसे किसी भी वर्ण से मेल खाए जो ब्रैकेट में नहीं है).

विकल्प (ज़रूरी नहीं)

re.sub(pat, replace, str) फ़ंक्शन दी गई स्ट्रिंग में पैटर्न के सभी इंस्टेंस खोजता है और उन्हें बदल देता है. बदली गई स्ट्रिंग में '\1', '\2' शामिल हो सकता है जो मिलते-जुलते ओरिजनल टेक्स्ट के ग्रुप(1), ग्रुप(2) वगैरह के टेक्स्ट से मिलते हैं.

यहां एक उदाहरण दिया गया है, जिसमें सभी ईमेल पते खोजे जाते हैं और उपयोगकर्ता (\1) को बनाए रखने के लिए उसमें बदलाव किया जाता है, लेकिन yo-yo-dyne.com को होस्ट के तौर पर सेट किया जाता है.

  str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher'
  ## re.sub(pat, replacement, str) -- returns new string with all replacements,
  ## \1 is group(1), \2 group(2) in the replacement
  print(re.sub(r'([\w\.-]+)@([\w\.-]+)', r'\1@yo-yo-dyne.com', str))
  ## purple alice@yo-yo-dyne.com, blah monkey bob@yo-yo-dyne.com blah dishwasher

कसरत

रेगुलर एक्सप्रेशन की प्रैक्टिस करने के लिए, शिशुओं के नाम की कसरत लेख पढ़ें.