রেগুলার এক্সপ্রেশন

রেগুলার এক্সপ্রেশন #

যে স্পেশাল ক্যারেক্টার-সিকুয়েন্স ব্যবহার করে এক বস্তা স্ট্রিং থেকে মহানায়ক অনন্ত জলিলের খোঁজ দ্যা সার্চকে কাজে লাগিয়ে নির্দিষ্ট কোন প্যাটার্ন বা সিকোয়েন্সের স্ট্রিং খুঁজে বের করা হয় প্রোগ্রামিংয়ের ভাষায় তাকে Regular Expression (রেগুলার এক্সপ্রেশন) বলে। অনেক সময় একে সংক্ষেপে regex বা regexp অথবা অতি সংক্ষেপে RE বলেও ডাকা হয়।

উইকিপিডিয়ার ভাষ্যমতে,

থিওরিটিক্যাল কম্পিউটার সায়েন্স ও ফরমাল ল্যাঙ্গুয়েজ থিওরি অনুযায়ী, রেগুলার এক্সপ্রেশন (র্যা শনাল এক্সপ্রেশন নামেও পরিচিত) হলো, ক্যারেক্টারের সিক্যুয়েন্স যা একটি সার্চ প্যাটার্নকে নির্দেশ করে, বিশেষ করে স্ট্রিং দিয়ে প্যাটার্ন ম্যাচিং করা অথবা স্ট্রিং ম্যাচিং করা। যেমন “find and replace” অপারেশন।

আমরা যারা সাধারণ মস্তিষ্কের মানুষজন আমাদের অবশ্য এতটা কঠিন কঠিন সংজ্ঞা জানার দরকার নাই। আমরা কাজ করতে করতে শিখব। এবার নিচের স্ট্রিংটা একটু লক্ষ্য করি।

purple [email protected], blah monkey [email protected] blah dishwasher

এইখানে তো দুইটা ইমেইল এড্রেস আছে, দুই চক্ষে সবাই দেখতে পাইতেছি। ধরেন, আমরা এই ইমেইল দুইটা পাইথন দিয়া খুঁজে বের করব। এজন্য আমরা re নামের একটা স্টান্ডার্ড মডিউল ব্যবহার করব।

import re

my_string = "purple [email protected], blah monkey [email protected] blah dishwasher"
temp = my_string.split(',')
for phrase in temp:
    result = re.search("([\w\.-]+)@([\w\.-]+)", phrase)
    print(result.group())

আউটপুট

alice@google.com
bob@abc.com

হুম, আমরা দুইটা ইমেইল খুঁজে পেয়েছি। কিন্তু কিভাবে কি করলাম? হুম, চিন্তার বিষয়। re.search("([\w\.-]+)@([\w\.-]+)", phrase) অংশটা খেয়াল করি সবাই। re.search(pattern, string, flags=0) ফাংশনের প্রথম আর্গুমেন্ট হল প্যাটার্ন (যে প্যাটার্ন ধরে আমরা খোঁজ দ্যা সার্চ চালাব), দ্বিতীয় আর্গুমেন্ট হল স্ট্রিং (যার ভিতরে খোঁজ দ্যা সার্চ চালাব), তৃতীয়টা আপাতত আমাদের মাথাব্যথার কারণ নয়। আমাদের মাথাব্যথার মূল কারণ এই প্যাটার্নটা। এটা কিভাবে লিখলাম? চলুন, সিঙ্গেল ক্যারেক্টার ম্যাচ (match) করার বেসিক প্যাটার্নগুলো শিখে নেয়া যাক।

প্যাটার্নবর্ণনা
A, a, 0আলফানিউমেরিক ক্যারেক্টার, শুধুই নিজেদের ম্যাচ করে।
.ডট চিহ্ন ডিফল্টভাবে ‘নিউ লাইন’ বাদে যেকোন ক্যারেক্টারকেই ম্যাচ করে। আর DOTALL ফ্লাগ থাকলে ‘নিউ লাইন’ও ম্যাচ করে।
^ক্যারেট স্ট্রিংয়ের শুরুটা ম্যাচ করে। আর MULTILINE মোডে থাকলে প্রতিটা নতুন লাইনের শুরুটাও ম্যাচ করে।
$স্ট্রিংয়ের শেষটা ম্যাচ করে। আর MULTILINE মোডে থাকলে নিউলাইনের আগের স্ট্রিংয়ের শেষটা ম্যাচ করে।
*ab* a, ab আর a এর পরে যতগুলো bই থাকুক না কেন সবগুলোকেই ম্যাচ করবে।
+ab+ a এর পরে যতগুলো bই থাকুক না কেন সবগুলোকেই ম্যাচ করবে কিন্তু কখনোই শুধু a কে ম্যাচ করবে না।
?ab? হয় a কে নয়তো ab কে ম্যাচ করবে।
*?, +?, ??*, +, ? হল লোভী টাইপের, যথাসম্ভব বেশি স্ট্রিং ম্যাচ করে। এদের লাগাম টেনে ধরার জন্যই ? কে এদের সাথে জুড়ে দেয়া হয়।
{m}a{m} কোন স্ট্রিংয়ের ঠিক m সংখ্যক a কে ম্যাচ করবে।
{m, n}a{m, n} কোন স্ট্রিংয়ের ঠিক m থেকে n সংখ্যক a কে ম্যাচ করবে।
{m, n}?a{m, n}? কোন স্ট্রিংয়ের ঠিক m সংখ্যক a কে ম্যাচ করবে, n সংখ্যক নয়।
/নরমালি স্পেশাল ক্যারেক্টারদের স্কেপ করে অথবা কোন স্পেশাল সিকুয়েন্সকে নির্দেশ করে।
[]ক্যারেক্টারের সেট বুঝায়। যেমন: [amk] a, m অথবা k কে ম্যাচ করবে। সেটের ভিতর দুইটা ক্যারেক্টারকে - চিহ্ন দিয়ে সেপারেট করা হলে ক্যারেক্টারের রেঞ্জ বুঝায়। যেমন: [a-z] a থেকে Z পর্যন্ত যেকোন লোয়ার কেসকে ম্যাচ করবে।
(...)ব্রাকেটের ভিতরে থাকা পুরা এক্সপ্রেশনটাই ম্যাচ করবে।
\Aশুধুমাত্র স্ট্রিংয়ের শুরুতে ম্যাচ করে।
\bযেকোন খালি স্ট্রিং ম্যাচ করে, কিন্তু শুধুমাত্র তখনই যখন তা শুরুতে বা শেষে থাকে।
\Bযেকোন খালি স্ট্রিং ম্যাচ করে, কিন্তু শুধুমাত্র তখনই যখন তা শুরুতে বা শেষে থাকে না।
\dশুধুমাত্র ইউনিকোড ডেসিমাল ডিজিট ম্যাচ করে।
\Dইউনিকোড ডেসিমাল ডিজিট বাদে সব ক্যারেক্টারকে ম্যাচ করে।
\sশুধুমাত্র ইউনিকোড হোয়াইটস্পেস ক্যারেক্টার (space, newline, return, tab) ম্যাচ করে।
\Sইউনিকোড হোয়াইটস্পেস ক্যারেক্টার বাদে সব ক্যারেক্টারকে ম্যাচ করে।
\wশুধুমাত্র ইউনিকোড ওয়ার্ড ক্যারেক্টার ম্যাচ করে।
\Wইউনিকোড ওয়ার্ড ক্যারেক্টার বাদে সব ক্যারেক্টারকে ম্যাচ করে।
\Zস্ট্রিংয়ের শেষটা ম্যাচ করে।
\t, \n, \rযথাক্রমে ট্যাব, নিউ লাইন ও রিটার্নকে ম্যাচ করে।

এরকম জটিল জিনিস আমাদের ছোট্ট মস্তিষ্কে ঢোঁকার কথা না। আসলে ঢোঁকার দরকারও নাই। ব্যাপক প্রাকটিসের মাধ্যমে আমরা এগুলো আত্মস্থ করে নেব। এবার আমরা re মডিউলের কিছু ফাংশনের ব্যবহার আর উপরের সার্চ প্যাটার্নগুলোর মজার সব উদাহরণ দেখব।

re.match(pattern, string, flags=0) #

প্যাটার্ন আর স্ট্রিং - আর্গুমেন্ট দুইটার মানে তো আমরা জানিই। এবার দেখা যাক flags জিনিসটা কি। flag হল বিভিন্ন মডিফায়ার ফাংশন যারা রিজেক্সে প্রভাব-পরিবর্তন সৃষ্টি করে। এরকম কিছু flag হল re.A বা re.ASCII, re.I বা re.IGNORECASE, re.L বা re.LOCALE, re.M বা re.MULTILINE, re.S বা re.DOTALL এবং re.X বা re.VERBOSE। সামনে ধাপে ধাপে হয়ত আমরা এর সম্পর্কে অল্প-বিস্তর জানব।

এবার re.match() ফাংশনের কথায় আসি আবার। যদি রিজেক্স প্যাটার্ন স্ট্রিংয়ের শুরুতে শূন্য বা তার বেশি ম্যাচ খুঁজে পায় তাহলে এই ফাংশন একটা ম্যাচ অবজেক্ট রিটার্ন করে। আর ম্যাচ না করলে None রিটার্ন করে। আর group() বা group(num) মেথড ব্যবহার করে আমরা অবজেক্ট থেকে বিভিন্ন ঢঙে ভ্যালু পেতে পারি।

import re

my_string = "purple [email protected], blah monkey [email protected] blah dishwasher"
temp = my_string.split(',')
for phrase in temp:
    result = re.match("([\w\.-]+)@([\w\.-]+)", phrase)
    print(result)

আউটপুট

None
None

None রিটার্ন করেছে। এর কারণ হল match() ফাংশন শুধুমাত্র স্ট্রিংয়ের শুরুতে খোঁজ দ্যা সার্চ করে ম্যাচিং স্ট্রিং বের করে। আর search() ফাংশন পুরো স্ট্রিং জুড়েই খোঁজ দ্যা সার্চ করে ম্যাচিং স্ট্রিং বের করে। এজন্য শুরুর উদাহরণে উপযুক্ত ফলাফল পেলেও এবার পেয়েছি ঘোড়ার ডিম।

re.sub(pattern, repl, string, count=0, flags=0) #

এই ফাংশন স্ট্রিংয়ের কোন অংশ প্যাটার্নকে ম্যাচ করলে তা repl দিয়ে রিপ্লেস করে। আর প্যাটার্ন ম্যাচ না করলে কিছুই হয় না।

>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'

বিষয়টা মনে হয় অনেক কঠিন হয়ে গেল। তাহলে এটুকুই থাক। আমরা শুধুমাত্র ধারণা নিলাম একটা। আরেকটু বড় হয়ে আমরা বিস্তারিত ধারণা নেব। এজন্য সবাই পাইথনের অফিসিয়াল ডক ফলো করতে পারি।

মন্তব্য করুন