درس ۱۵: عبارات با قاعده (Regular Expression) یا RegEx در پایتون¶

Photo by Ehud Neuhaus¶
عبارات با قاعده (Regular expression) یا به اختصار RegEx، رشتهای حاوی کاراکترهایی خاص و با معنی است که در عملیاتهایی مانند یافتن (find)، جاگذاری (repleace) و اعتبارسنجی (validation) به شدت کاربرد پیدا کرده است. در واقع با استفاده از RegEx میتوان یک الگو (pattern) برای جستجو در متن یا تطابق آن ایجاد کرد. [ویکیپدیا]
در این درس به شرح قواعد موجود در RegEx و همچنین ماژول re
از کتابخانه استاندارد پایتون خواهیم پرداخت [اسناد پایتون]. ماژول re
یک مجموعه ابزار برای کار با RegEx در پایتون را فراهم آورده است که میتوان آن را موتور تحلیل RegEx در پایتون دانست.
✔ سطح: متوسط
عبارات باقاعده¶
قواعد «عبارات باقاعده» شامل استفاده و چیدمان تعدادی کاراکتر خاص میشود که در مجموع معنی یا الگویی را به وجود میآورند!. در ادامه به بررسی این کاراکترها میپردازیم.
کاراکترهای تطابق (Matching Characters)¶
در عمل تطبیق (Match) با RegEx، اکثر حروف و کاراکترها معرف خود همان کاراکتر است. برای مثال کاراکتر a
معرف a
است و کلمه test
نیز با همان کلمه test
تطابق یا همخوانی پیدا میکند.
با این حال برخی از کاراکترها هستند که به جای همخوانی با خود، مفهوم دیگری را میرسانند که از آنها با عنوان metacharacters
(متاکاراکترها) یاد میشود [اسناد پایتون] و عبارتند از:
. ^ $ * + ? [] {} | () \
Dot
.
به معنی «هر کاراکتری» میباشد. یعنی وجود
.
در یک الگو (Pattern) باعث انجام عمل تطابق یا همخوانی با هر کاراکتری خواهد شد (به جز کاراکتر newline یاn\
).نکته
به صورت پیشفرض
.
در الگوی RegEx کاراکتر newline را شامل نمیشود ولی در زبان برنامهنویسی پایتون میتوان با نشانهگذاریre.DOTALL
کاری کرد که کاراکتر newline را نیز شامل شود. نمونه کد مربوط به این بحث در بخش بعدی بررسی خواهد شد.به عنوان نمونه الگوی
...
هر سه کاراکتر متوالی را در بر میگیرد:...
'ab' => no matche 'abc1234' => 2 matches ('abc', '123') '01267' => 1 matche ('012') 'python' => 2 matches ('pyt', 'hon') '2 0 85 ad' => 3 matches ('2 0', ' 85', ' a')
Caret
^
در یک کاربرد در سمت چپ یک الگو قرار میگیرد و تعین کننده الزام شروع با آن الگو (Pattern) میباشد (کاربرد دیگر آن در ادامه ذکر خواهد شد).
^a
'a' => 1 matche ('a') 'abc' => 1 matche ('a') 'bac' => no matche ('a' is not in the start)
^ab
'abc' => 1 matche ('ab') 'acb' => no matche (starts with 'a' but not followed by 'b') 'ab123 ab456' => 1 matche ('ab' from ab123)
Dollar
$
تعین کننده نقطه پایان تطابق الگو میباشد.
a$ a => Matched formula => Matched cab => NO Match ('a' is not in the end)
^s...d$
به عنوان مثالی دیگر، الگوی بالا معرف هر کلمه پنج کاراکتری است که با کاراکتر
s
شروع و با کاراکترd
پایان میپذیرد.Star
*
معرف هیچ یا هر تعداد تکرار الگوی سمت چپ خود میباشد.
ma*n mn => Matched man => Matched maaan => Matched main => NO Match ('a' is not followed by 'n') woman => Matched
Plus
+
معرف یک یا هر تعداد تکرار الگوی سمت چپ خود میباشد.
ma+n mn => NO Match (no 'a' character) man => Matched maaan => Matched main => NO Match ('a' is not followed by 'n') woman => Matched
Question Mark
?
معرف هیج یا یک تکرار الگوی سمت چپ خود میباشد.
ma?n mn => Matched man => Matched maaan => NO Match (more than one 'a' character) main => NO Match ('a' is not followed by 'n') woman => Matched
Square brackets
[ ]
از دو کاراکتر
[
و]
برای بیان یک دسته یا بازه از کاراکترها استفاده میشود که برای این منظور میتوان تک تک کاراکترها را به صورت صریح نوشت یا با استفاده از کاراکتر-
به سادگی یک بازه را تعیین نمود.[abc]
برای نمونه، الگوی بالا با هر رشتهای که حاوی کاراکترهای
b
،a
یاc
باشد تطابق خواهد داشت [regex101@ تست آنلاین].[abc] a => 1 Matched ac => 2 Matched python => NO Match abc de ca => 5 Matched
همانطور که بیان شد، با استفاده از کاراکتر
-
هر دو بخش روبرو در نمونه کد پایین با یکدیگر معادل و برابر هستند:[0-9] == [0123456789] [1-4] == [1234] [a-e] == [abcde] [1-5a-e] == [12345abcde] [0-39] == [01239] [a-eA-E] == [abcdeABCDE]
توجه
باید توجه داشت RegEx ذاتا CASE SENSITIVE است و کوچک یا بزرگ بودن حروف انگلیسی در آن تاثیر دارد. با این حال زبانهای برنامهنویسی از جمله پایتون امکاناتی را برای غیرفعال ساختن این وضعیت فراهم میآورند که در ادامه بررسی خواهد شد.
کاربرد دوم کاراکتر ^ (Caret)
اکنون زمان آن رسیده است تا با کاربرد دوم کاراکتر Caret
^
آشنا شویم: با قرار دادن این کاراکتر به عنوان عضو ابتدای یک[ ]
میتوان عملکرد یا مفهوم آن را برعکس یا در واقع NOT کرد! برای نمونه الگوی[abc^]
به معنی تطابق با هر کاراکتری است به جزb
،a
وc
. [regex101@ تست آنلاین]Braces
{ }
با ساختاری مشابه
{n,m}
به کار میرود که در آنn
وm
به ترتیب بیان کننده حداقل و حداکثر تعداد تکرار الگوی سمت چپ خود میباشند.a{2,3} abc dat => NO Match abc daat => 1 Matched (at d'aa't) aabc daaat => 2 Matched (at 'aa'bc and d'aaa't) aabc daaaat => 2 Matched (at 'aa'bc and d'aaa'at)
[0-9]{2,4} 1 => NO Match ab123cde => 1 Matched (at ab'123'cde) 12 => 1 Matched 123456789 => 2 Matched (at '1234' and '5678') 1 2 3 4 => NO Match
چنانچه از
{ }
با ساختاری مشابه{n}
به کار برود، مفهوم تعداد تکرار دقیق (لازم) الگوی سمت چپ خود را میرساند.[0-9]{3} 1 => NO Match ab123cde => 1 Matched (at ab'123'cde) 12 => NO Match 123456789 => 3 Matched (at '123' and '456' and '789') 1 2 3 4 => NO Match
Pipe
|
این کاراکتر مفهومی معادلی عملگر
OR
(یا) دارد که معرف تطابق با الگوی سمت راست یا سمت چپ خود میباشد.a|b cde => NO Match ade => 1 Matched (at 'a'de) acdbea => 3 Matched (at 'a'cd'b'e'a')
Parentheses
()
برای گروهبندی الگوها از پرانتز استفاده میشود یعنی میتوان الگویی را با استفاده از یک یا چند زیرالگو (sub-patterns) ایجاد کرد.
(a|b|c)xz ab xz => NO Match abxz => 1 Matched (at a'bxz') axz cabxz => 2 Matched (at 'axz'bc ca'bxz')
این الگو تمامی حروفی که شامل یکی از کاراکترهای
a
یاb
یاc
بوده و در ادامهxz
باشد را تطبیق میدهد.Backslash
\
از این کاراکتر برای بیاثر کردن مفهوم هر یک از metacharacters در الگو استفاده میشود. برای نمونه قرار گرفتن
*\
در یک الگو، تنها خود کاراکتر*
را تطبیق میدهد و به عبارتی دیگر کاراکتر*
در این جا مفهوم الگویی خود (تکرار الگو سمت چپ) را از دست دا��ه است.[0-9]\.[0-9]{2} 3 => NO Match 3.55 => 1 Matched (at '3.55') 5.2 => NO Match 7.37520 => 1 Matched (at '7.37') 506.035 => 1 Matched (at '6.03')
توالیهای ویژه (Special Sequences)¶
در بحث عبارات باقاعده هنگامی که کاراکتر \
به همراه یک کاراکتر مشخص (به شرح زیر) آورده شود، Special sequence خوانده میشود. Special sequences برای سهولت در نوشتن الگوها کاربرد دارند که برخی از پر کاربردترین آنها عبارتند از:
\A \b \B \d \D \s \S \w \W \Z
A\
برای تعین شروع یک متن به کار میرود. برای نمونه، الگوی
AThe\
تمام رشتههایی که باThe
شروع شوند را تطبیق میدهد (یادآوری: در بحث RegEx، کوچک یا بزرگ بودن حروف دارای اهمیت است).\AThe The Rain => Match In The Rain => NO Match
توجه
تفاوت
A\
و کاربرد یکم^
: در متنهای چند سطری مشاهده میشود به گونهای کهA\
به ابتدای کل آن متن و^
به ابتدای هر سطر از متن اشاره میکنند.b\
بر حسب موقعیت قرار گرفتن، شروع یا پایان یک کلمه را مشخص میکند.
\bunder understand => Match underworld => Match Underworld => NO Match TheUnderworld => NO Match
ing\b Driving => Match Spring => Match spring_day => NO Match
نکته
این کاراکتر (
b\
) در مبحث RegEx به عنوان انطباقگر یک word(\w
) boundary نیز خوانده میشود. word boundary (مرز واژه) به سه موقعیت گفته میشود:قبل از نخستین کاراکتر، زمانی که کاراکتر نخست یکی از کاراکترهای شامل
w\
باشدبعد از کاراکتر پایانی، زمانی که کاراکتر پایانی یکی از کاراکترهای شامل
w\
باشدبین دو کاراکتر، زمانی که یکی از این دو کارامتر شامل
w\
باشد ولی دیگری خیر
[regex101@ تست آنلاین] در این نمونه کد، نقاط word boundary مشخص شده است
در ادمه کارکتر
w\
شرح داده خواهد شد.B\
برعکس
b\
، بر حسب موقعیت قرار گرفتن، شروع یا پایان نیافتن یک کلمه را مشخص میکند. یعنی کلماتی را تطبیق میدهد که با کاراکتر یا کارکترهایی مشخصی شروع یا پایان نیافته باشند.\Bunder understand => NO Match underworld => NO Match Underworld => NO Match Thunderbird => Match
ball\B Football => NO Match Footballist => Match
نکته
این کاراکتر (
B\
) در تضاد باb\
به عنوان انطباقگر نقاطی که word(\w
) boundary نیستند، نیز خوانده میشود. [regex101@ تست آنلاین]d\
معادل
[9-0]
\d{3,5} 1 => NO Match 123 => 1 Matched (at '123') 123456 => 1 Matched (at '12345') 1237 Main Street, => 1 Matched (at '1237')
D\
برعکس
d\
- معادل[9-0^]
، یعنی تمامی کاراکترهای غیر عددی را تطبیق میدهد.\D{3,5} 1 => NO Match 123 => NO Match 123456 => NO Match 1237 Main Street, => 3 Matched (at ' Main', ' Stre', 'et,')
s\
معادل
[t\n\r\f\v\ ]
، به معنی عمل تطبیق با کاراکتر فضای خالی است.\s 1237 Main Street, => 2 Matched (2 spaces)
البته باید توجه داشته که منظور از کاراکترهای
t \n \r \f \v\
همان Escape character هستند [ویکیپدیا].\n ---> new line \r ---> carriage return \t ---> tab \v ---> vertical tab \f ---> form feed
S\
برعکس
s\
- معادل[t\n\r\f\v\ ^]
، به معنی عمل تطبیق با هر کاراکتری غیر از فضای خالی است.\S{2,5} 1237 Main Street, => 4 Matched (at '1237', 'Main', 'Stree', 't,')
w\
معادل
[_a-zA-Z0-9]
، به معنی عمل تطبیق با کاراکترهای الفبایی و عددی (زبان انگلیسی) به همراه کاراکتر_
یا underscore.\w{2,5} 1237 Main Street, => 3 Matched (at '1237', 'Main', 'Stree')
W\
برعکس
w\
- معادل[_a-zA-Z0-9^]
، به معنی عمل تطبیق با کاراکتری به غیر از کاراکترهای الفبایی و عددی (زبان انگلیسی) به همراه کاراکتر_
یا underscore.\W 1237 Main Street, => 3 Matched (2 spaces and ',')
Z\
برای تعین پایان یک متن به کار میرود. برای نمونه، الگوی
Rain\Z
تمام رشتههایی که باRain
پایان یابند را تطبیق میدهد (یادآوری: در بحث RegEx، کوچک یا بزرگ بودن حروف دارای اهمیت است).Rain\Z The Rain => Match
[regex101@ تست آنلاین] [regex101@ تست آنلاین]
توجه
تفاوت
Z\
و$
: در متنهای چند سطری مشاهده میشود به گونهای کهZ\
به انتهای کل آن متن و$
به انتهای هر سطر از متن اشاره میکنند.
انطباق با طول صفر (Zero-length Match)¶
یک عبارت Zero-length به عبارتی از مبحث RegEx گفته میشود که هیچ کاراکتری را انطباق نمیدهد بلکه تنها موقعیتهایی را در متن رشته مورد نظر تطبیق میدهد. برای نمونه متن رشته 1,2
را درنظر بگیرید، الگو b\
تنها موقعیتهای قبل از 1
، بین 1
و ,
، بین ,
و 2
و بعد از 2
را تطبیق میدهد؛ در نتیجه الگو b\
برای این متن یک Zero-length Match است. [به نتایج دقت کنید: regex101@ تست آنلاین] . Zero-length Match در RegEx بسیار گمراه کننده هستند و میبایست با دقت از آنها استفاده کرد. در زیر چند نمونه الگو از Zero-length Matchها ارايه شده است:
\b
\B
\A
\B
^
$
.*
\d*
سعی کنید برای الگوهای بالا در regex101.com، متنهایی را مثال بزنید، تست کنید و نتایج را با دقت مشاهده نمایید، به عنوان مثالی دیگر الگو زیر را در نظر بگیرید - این مورد را با هم بررسی میکنیم:
p*
حاصل انطباق این الگو برای متنی مانند 1,2
هیچ فرق با الگو b\
(که پیشتر صحبت شد) ندارد - تا اینجا فهمیدیم که از دید RegEx متن تنها همان کاراکترهای قابل دید نیست بلکه نقاطی مثل قبل از کاراکتر نخست، بعد از کاراکتر پایانی و حتی گاهی بین دو کاراکتر را نیز در نظر میگیرد و همچنین میدانیم که این الگو به دنبال هیچ یا هربار تکرار کاراکتر p
در متن است. در نتیجه نقاطی که شامل هیچ کاراکتری نباشد یا شامل کاراکتر p
باشد تطبیق پیدا میکند و از آنجایی که متن مورد نظر شامل p
نمیباشد، تنها موقعیتهای Zero-length باقی میماند! - این الگو برای متن یاد شده کاملا یک Zero-length Match است.
[regex101@ تست آنلاین] (p در این الگو تنها یک کاراکتر متنی است - کاراکترهای دیگر را نیز خودتان تست کنید)
ماژول re پایتون¶
آنچه از عبارات باقاعده تاکنون آشنا شدیم تنها شامل تعدادی تعاریف و قواعد بودند که برای استفاده و به کار بردن آنها در زبانهای برنامهنویسی نیاز به ابزارهایی میباشد. همچنین باید توجه داشت انجام تمامی امور مربوط به پردازش متن را نباید از عبارات باقاعده انتظار داشت چرا که این قواعد هم محدودیتهای خاص خود را دارد و در مواردی ممکن است الگوی ایجاد شده چنان پیچیده گردد که از خوانایی برنامه کاسته شود.
در زبان برنامهنویسی پایتون از طریق ماژول re
از کتابخانه استاندارد آن، ثابتها (constants) و توابع (functions) کاربردی بسیاری در زمینه عبارت باقاعده فراهم آورده شده است. در ادامه نگاهی کوتاه به این ماژول خواهیم داشت و شرح کامل توابع کاربردی آن به درس بعدی سپرده خواهد شد.
تابع compile
و شی Regular Expression پایتون¶
re.compile(pattern, flags=0)
تابع compile
یک الگوی RegEx را کامپایل و یک شی RegEx پایتونی [اسناد پایتون] برمیگرداند. [اسناد پایتون]
این تابع دو پارامتر دارد: pattern
که معرف الگو RegEx مورد نظر میباشد و flags
که با توجه به داشتن مقدار پیشفرض 0
، ارسال آن اختیاری است.
پیش از ادامه با این تابع، بهتر است با مقادیر مجاز برای پارامتر flags
آشنا شویم، چرا که این پارامتر اختیاری در توابع دیگری از ماژول re
نیز مورد استفاده قرار گرفته است. در واقع با استفاده از این پارامتر میتوان چگونگی پردازش الگو را دستخوش تغییراتی ساخت که گاهی ممکن است بسیار کارگشا باشند. [اسناد پایتون]:
re.I
یاre.IGNORECASE
نادیده گرفتن حروف کوچک یا بزرگ - یعنی صرف نظر از اینکه در الگوی مورد نظر از کاراکتر بزرگ استفاده شده باشد یا کوچک، عمل انطباق با آن کاراکتر صورت پذیرد. [اسناد پایتون]
re.M
یاre.MULTILINE
سطرها در نظر گرفته شوند - در حالت عادی کاراکترهای تطابق
^
و$
در الگو RegEx مشخص کننده ابتدا و انتهای یک رشته یا متن میباشند. فارغ از اینکه متن مورد نظر میتواند شامل چندین سطر باشد عمل تطابق با کل متن به انجام میرسد. اما با استفاده از این flag میتوان مفهوم سطر را نیز پر رنگ کرد، به این صورت که: کاراکتر^
مشخص کننده ابتدای هر سطر و کاراکتر$
نیز مشخص کننده انتهای هر سطر خواهد بود. [اسناد پایتون]
re.S
یاre.DOTALL
کاراکتر
.
شامل تمامی کاراکترها باشد - در حالت عادی این کاراکتر عمل تطابق با کاراکتر سطر جدیدn\
را شامل نمیشود که با استفاده از این flag این ویژگی به الگو اضافه میگردد. [اسناد پایتون]
re.X
یاre.VERBOSE
الگو حاوی توضیح (Comment) است - با استفاده از این flag میتوان توضیح را به الگو اضافه کرد که نقش زیادی در بالا بردن خوانایی و درک الگو ایجاد میکند. [اسناد پایتون]
نکته
میتوان با استفاده از کاراکتر
|
در زمان ارسال آرگومان به flags، همزمان چندین مقدار را تنظیم نمود. به مانند:re.compile(pattern, re.I | re.M)پارامتر flags میتواند مقادیر دیگری نیز بپذیرد که برای مطالعه بیشتر میتوانید به مستندات پایتون مراجعه نمایید.
به تابع compile
برمیگردیم:
>>> import re
>>>
>>> pattern = re.compile("^<html>", re.I)
>>>
>>> type(pattern)
<class 're.Pattern'>
>>>
>>> print(pattern)
re.compile('^<html>', re.IGNORECASE)
کاربرد تابع compile
زمانی است که میخواهیم از یک الگو مشخص چندین بار در طول اجرای یک ماژول استفاده نماییم. در ادامه با توابع دیگری از ماژول re
آشنا خواهیم شد؛ این توابع پیش از انجام وظیفه مربوط به خود، به صورت ضمنی الگو را به شی re.Pattern
کامپایل میکنند که به این صورت میتوان کارایی برنامه را با یک بار compile
افزایش دهیم. البته باید اشاره کرد که مفسر پایتون به صورت خودکار نمونه کامپایل شده چند الگو آخر مورد استفاده را Cache میکند، بنابراین چنانچه در برنامه خود از تعداد اندکی الگو استفاده میکنید، میتوانید در این زمینه نگران کارایی نباشید.
یادآوری شود که با استفاده از تابع dir
میتوانیم متدها و صفتهای شی الگو را مشاهده نماییم:
>>> dir(pattern) # Python 3.x
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'findall', 'finditer', 'flags', 'fullmatch', 'groupindex', 'groups', 'match', 'pattern', 'scanner', 'search', 'split', 'sub', 'subn']
>>>
الگوهای گسترده (Extended Patterns) پایتون¶
در اینجا به عنوان بخش پایانی این درس به بحث پیرامون چگونگی ایجاد ساختار الگو در RegEx پایتون برمیگردیم و به معرفی ساختارهای کاربردی دیگری که توسط این زبان پشتیبانی میشود میپردازیم.
علاوه بر قواعد مورد بحث در دو بخش ابتدایی این درس، الگو (pattern) در RegEx پایتون میتواند شامل ساختارهایی باشد که با یک ؟)
شروع و با یک (
پایان مییابند - مانند: ( ?)
. این نوع ساختار از زبان پرل (Perl) الگوبرداری شده است. در این نوع ساختارها نخستین کاراکتر بعد از ؟
چگونگی عملکرد آن ساختار در الگو را مطرح میکند. توجه داشته باشید که با وجود پرانتز در این ساختارها، جز در یک مورد - هیچکدام از آنها معنی گروهبندی را نمیدهند و پرانتز در اینجا صرفا مشخص کننده یک فرمان خاص یا محدوده اثر ساختار میباشد. این ساختارها عبارتند از:
(aiLmsux?)
این ساختار کمک میکند که بتوانیم یک یا چند مقدار از پارامتر flags را که پیشتر با آن آشنا شدیم (مانند
re.M
که در اینجا معادل(m?)
است) را از طریق متن الگو اثر دهیم و نه با ارسال پارامتر - باید توجه داشته باشید که برخلاف زبان پرل نمیتوان این ساختار را در پایتون به صورت حوزهای و محدود استفاده کرد بلکه همانند ارسال پارامتر، این فرامین به صورت کلی در الگو اثرگذاری خواهند داشت:(?i)PYTHON PROGRAMING The Python Programing Language => 1 Matched ('Python Programing')
در نمونه کد بالا، وجود ساختار
(i?)
باعث نادیده گرفتن حروف کوچک یا بزرگ شده است - همانند ارسال پارامترre.I
[regex101@ تست آنلاین]'a' == re.A (ASCII-only matching) 'i' == re.I (ignore case) 'L' == re.L (locale dependent) 'm' == re.M (multi-line) 's' == re.S (dot matches all) 'u' == re.U (Unicode matching) 'x' == re.X (verbose)
به ضعف این ساختار در محدودسازی در یک حوزه از الگو اشاره شد، از پایتون نسخه 3.6 به بعد میتوان از ساختار منعطفتر زیر استفاده کرد.
(...:aiLmsux-imsx?)
از پایتون نسخه 3.6 به بعد میتوان از این ساختار استفاده کرد. با استفاده از این ساختار میتوان اثربخشی حضور پارامترهای flags را محدود به ناحیه خاصی از الگو کرد به این صورت که این بخش از الگو میبایست بعد از کاراکتر
:
موجود در ساختار قرار بگیرد:^The (?i:PYTHON PROGRAMMING) Language$
مطابق الگو بالا، مهم نیست بخش
PYTHON PROGRAMMING
در متن مورد نظر ما با چه ترتیبی از کوچک یا بزرگ بودن حروف حضور داشته باشد، همین که در جای مناسب خود باشد کافی است. اما دو کلمهThe
وLanguage
میبایست عینا حضور داشته باشند:>>> import re # PYTHON 3.7.3 >>> pattern = re.compile("^The (?i:PYTHON PROGRAMMING) Language$") >>> print("YES") if pattern.match("The Python Programming Language") else print("NO") YES >>> print("YES") if pattern.match("The Python Programming LANGUAGE") else print("NO") NO
در درس بعدی با تابع
match
آشنا خواهید شد. به صورت خلاصه، این تابع مقدار دریافتی را با الگو تطابق میدهد و در صورت شکست مقدارNone
برمیگرداند.قابلیت دیگر این ساختار: میتوان با قرار دادن یک کاراکتر
-
قبل از پارمترهایi
m
s
x
، اثر بخشی آنها را در حوزه مشخصی از الگو غیرفعال ساخت:>>> import re # PYTHON 3.7.3 >>> pattern = re.compile("^The (?-i:PYTHON PROGRAMMING) Language$", re.I) >>> print("YES") if pattern.match("The PYTHON PROGRAMMING Language") else print("NO") YES >>> print("YES") if pattern.match("The PYTHON PROGRAMMING LANGUAGE") else print("NO") YES >>> print("YES") if pattern.match("THE PYTHON PROGRAMMING LANGUAGE") else print("NO") YES >>> print("YES") if pattern.match("THE Python Programming LANGUAGE") else print("NO") NO
پرانتزگذاری غیرقابل پیگیری (non-capturing):
(...:?)
این ساختار نمایش پرانتزگذاری یا همان گروهبندی معمولی در بحث RegEx است ولی با این تفاوت که نتایج انطباق داخل پرانتز قابل بازیابی (همانند یک گروه معمولی) نخواهد بود:
>>> import re >>> string = "Python@1991" >>> pattern = "(\d+)" >>> match = re.search(pattern, string) >>> match <re.Match object; span=(7, 11), match='1991'> >>> match.group(1) '1991'
>>> import re >>> string = "Python@1991" >>> pattern = "(?:\d+)" >>> match = re.search(pattern, string) >>> match <re.Match object; span=(7, 11), match='1991'> >>> match.group(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: no such group
گروه بانام (Named Group):
(...<P<name?)
این ساختار نمایش همان گروهبندی عادی در بحث RegEx است با این تفاوت که میتوان به هر گروه یک نام انتساب داد. به کمک ماژول re پایتون، همانطوری که در درس بعدی به صورت کامل خواهید دید، حاصل انطباق هر گروه موجود در الگو با متن مورد نظر از طریق یک اندیس عددی که به ترتیب از یک شروع میشود قابل دسترسی است، با استفاده از این ساختار میتوان حاصل انطباق را بسیار خواناتر و تنها با استفاده از نام آن دستیابی نمود:
>>> import re >>> string = "NOV 7, 1987" >>> pattern = "^(\w+)\s(\d+),\s(\d+)$" >>> match = re.search(pattern, string) >>> match.group(1) 'NOV' >>> match.group(2) '7' >>> match.group(3) '1987'
>>> import re >>> string = "NOV 7, 1987" >>> pattern = "^(?P<month>\w+)\s(?P<day>\d+),\s(?P<year>\d+)$" >>> match = re.search(pattern, string) >>> match.group(1) 'NOV' >>> match.group(2) '7' >>> match.group(3) '1987' >>> match.group('month') 'NOV' >>> match.group('day') '7' >>> match.group('year') '1987'
تابع
search
به دنبال اولین انطباق pattern در string میگردد، در صورت موفقیت یک شیMatch
و در غیر این صورتNone
برمیگرداند - این تابع و خروجی آن در درس بعدی شرح داده میشود.(P=name?)
این ساختار امکان ارجاع به حاصل انطباق یک گروه بانام در آن الگو را فراهم میآورد. این ساختار در الگوهایی که میبایست دقیقا یک بخش از متن تکرار گردد، بسیار کاربردی است. به نمونه کد زیر توجه نمایید، در این مثال قرار است ایمیل مربوط به دو شخص بررسی شود که آیا بر اساس نام و نامخانوادگی آنها ایجاد شده است یا خیر:
>>> import re >>> strings = ["name family: [email protected]", "diff_name family: [email protected]"] >>> pattern = "(?P<f_name>\w+)\s(?P<l_name>\w+):\s(?P=f_name)\.(?P=l_name)@mail\.com$" >>> for string in strings: ... print('STRING:', string) ... match = re.search(pattern, string) ... if match: ... print('Matched:', match.group('f_name'), match.group('l_name')) ... else: ... print('No match!') ... STRING: name family: [email protected] Matched: name family STRING: diff_name family: [email protected] No match!
توضیحات (Comment)
(...#?)
همانند توضیح در پایتون، متن موجود در این ساختار، هنگام پردازش الگو نادیده گرفته میشود:
>>> import re >>> pattern = re.compile('^\d{3}:(?#Iran Emergency Numbers)\s\w+\.', re.I) >>> print("YES") if pattern.match("115: Ambulance.") else print("NO") YES
Positive Lookahead Assertion
(...=?)
این ساختار امکان ایجاد شرط مثبت، برای پیش از خود را در الگو فراهم میآورد. به این صورت که میتوان انطباق یک متن را منوط به برقرای شرط (انطباق) بعد از آن کرد. آن بخشی از الگو که قبل از این ساختار قرار دارد، تنها زمانی انطباق داده میشود که این ساختار منطبق باشد. باید توجه داشت که الگو مشخص شده درون این ساختار (
...
) به عنوان حاصل انطباق برگردانده نخواهد شد و صرفا برای برقرای شرط در الگو حضور خواهد داشت:>>> import re >>> pattern = re.compile("(\d{3}\.0)(?=00)") >>> print("YES") if pattern.match("115.099") else print("NO") NO >>> print("YES") if pattern.match("115.000") else print("NO") YES >>> match = pattern.search("115.000") >>> match.group() '115.0'
یکی از کاربردهای این ساختار، اعتبارسنجی گذرواژه است. به نمونه کد زیر توج�� کنید:
>>> pattern = re.compile("(?=\A\w{6,8}\Z)") >>> print("YES") if pattern.match("12345678") else print("NO") YES >>> print("YES") if pattern.match("123456") else print("NO") YES >>> print("YES") if pattern.match("123") else print("NO") NO
در این نمونه کد، از بخش قبل از ساختار صرف نظر شده است و بنابراین حاصل انطباقی نیز در کار نخواهد بود، همچنین با استفاده از دو
A\
وZ\
لزوم ابتدا و انتها الگو نیز مشخص شده است. حال به الگو بالا شرط دیگری اضافه میکنیم که گذرواژه حداقل شامل یک حرف الفبایی نیز باشد:>>> pattern = re.compile("(?=\A\w{6,8}\Z)(?=.*[a-zA-Z]+)") >>> print("YES") if pattern.match("d2345678") else print("NO") YES >>> print("YES") if pattern.match("2345ABcd") else print("NO") YES >>> print("YES") if pattern.match("12345678") else print("NO") NO
Negative Lookahead Assertion
(...!?)
معکوس حالت قبل است. به این معنی که انطباق یک متن منوط به عدم برقرای شرط (انطباق) بعد از آن است. آن بخشی از الگو که قبل از این ساختار قرار دارد، تنها زمانی انطباق داده میشود که این ساختار منطبق نباشد.
Positive Lookbehind Assertion
(...=>?)
این ساختار امکان ایجاد شرط مثبت، برای بعد از خود را در الگو فراهم میآورد. به این صورت که میتوان انطباق یک متن را منوط به برقرای شرط (انطباق) پیش از آن کرد. آن بخشی از الگو که بعد از این ساختار قرار دارد، تنها زمانی انطباق داده میشود که این ساختار منطبق باشد. باید توجه داشت که الگو مشخص شده درون این ساختار (
...
) به عنوان حاصل انطباق برگردانده نخواهد شد و صرفا برای برقرای شرط در الگو حضور خواهد داشت:>>> import re >>> match = re.search('(?<=abc)def', 'abcdef') >>> match.group() >>> 'def'
Negative Lookbehind Assertion
(...!>?)
معکوس حالت قبل است. به این معنی که انطباق یک متن منوط به عدم برقرای شرط (انطباق) قبل از آن است. آن بخشی از الگو که بعد از این ساختار قرار دارد، تنها زمانی انطباق داده میشود که این ساختار منطبق نباشد.
Yes/No Pattern
(?(id/name)yes-pattern|no-pattern)این ساختار نیز نوعی شرط گذاری است. به این صورت که میتوان تعیین کرد بر اساس وضعیت انطباق گروهبندیهای موجود (با ذکر نام گروه یا شماره اندیس آن) در الگو، یکی از الگوهای بله (yes-pattern) یا خیر (no-pattern) این ساختار بررسی شود:
>>> pattern = re.compile('^(###)?foo(?(1)bar|baz)') >>> print("YES") if pattern.match("###foobar") else print("NO") YES >>> print("YES") if pattern.match("###foobaz") else print("NO") NO >>> print("YES") if pattern.match("foobaz") else print("NO") YES
توجه
قالبهای Lookahead یا Lookbehind (در کل lookaroundها) را نیز میتوان از نوع Zero-Lengthها در نظر گرفت تنها تفاوت آنها با الگوهایی که پیشتر بیان شد این است که lookaroundها ابتدا کاراکتر(هایی) را تطبیق و سپس از آن(ها) گذر میکنند.
😊 امیدوارم مفید بوده باشه