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

عبارات با قاعده (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')
    

    [regex101@ تست آنلاین]

  • Caret ^

    در یک کاربرد در سمت چپ یک الگو قرار می‌گیرد و تعین کننده الزام شروع با آن الگو (Pattern) می‌باشد (کاربرد دیگر آن در ادامه ذکر خواهد شد).

    ^a
    
    'a'        => 1 matche ('a')
    'abc'      => 1 matche ('a')
    'bac'      => no matche ('a' is not in the start)
    

    [regex101@ تست آنلاین]

    ^ab
    
    'abc'           => 1 matche ('ab')
    'acb'           => no matche (starts with 'a' but not followed by 'b')
    'ab123 ab456'   => 1 matche ('ab' from ab123)
    

    [regex101@ تست آنلاین]

  • Dollar $

    تعین کننده نقطه پایان تطابق الگو می‌باشد.

    a$
          a        => Matched
          formula  => Matched
          cab      => NO Match ('a' is not in the end)
    

    [regex101@ تست آنلاین]

    ^s...d$
    

    به عنوان مثالی دیگر، الگوی بالا معرف هر کلمه پنج کاراکتری است که با کاراکتر s شروع و با کاراکتر d پایان می‌پذیرد.

  • Star *

    معرف هیچ یا هر تعداد تکرار الگوی سمت چپ خود می‌باشد.

    ma*n
          mn       => Matched
          man      => Matched
          maaan    => Matched
          main     => NO Match ('a' is not followed by 'n')
          woman    => Matched
    

    [regex101@ تست آنلاین]

  • Plus +

    معرف یک یا هر تعداد تکرار الگوی سمت چپ خود می‌باشد.

    ma+n
          mn       => NO Match (no 'a' character)
          man      => Matched
          maaan    => Matched
          main     => NO Match ('a' is not followed by 'n')
          woman    => Matched
    

    [regex101@ تست آنلاین]

  • 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
    

    [regex101@ تست آنلاین]

  • 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)
    

    [regex101@ تست آنلاین]

    [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
    

    [regex101@ تست آنلاین]

    چنانچه از { } با ساختاری مشابه {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
    

    [regex101@ تست آنلاین]

  • Pipe |

    این کاراکتر مفهومی معادلی عملگر OR (یا) دارد که معرف تطابق با الگوی سمت راست یا سمت چپ خود می‌باشد.

    a|b
          cde      => NO Match
          ade      => 1 Matched (at 'a'de)
          acdbea   => 3 Matched (at 'a'cd'b'e'a')
    

    [regex101@ تست آنلاین]

  • 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 باشد را تطبیق می‌دهد.

    [regex101@ تست آنلاین]

  • 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')
    

    [regex101@ تست آنلاین]

توالی‌های ویژه (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
    

    [regex101@ تست آنلاین]

    توجه

    تفاوت A\ و کاربرد یکم ^: در متن‌های چند سطری مشاهده می‌شود به گونه‌ای که A\ به ابتدای کل آن متن و ^ به ابتدای هر سطر از متن اشاره می‌کنند.

  • b\

    بر حسب موقعیت قرار گرفتن، شروع یا پایان یک کلمه را مشخص می‌کند.

    \bunder
    
          understand        => Match
          underworld        => Match
          Underworld        => NO Match
          TheUnderworld     => NO Match
    

    [regex101@ تست آنلاین]

    ing\b
    
          Driving        => Match
          Spring         => Match
          spring_day     => NO Match
    

    [regex101@ تست آنلاین]

    نکته

    این کاراکتر (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
    

    [regex101@ تست آنلاین]

    ball\B
    
          Football        => NO Match
          Footballist     => Match
    

    [regex101@ تست آنلاین]

    نکته

    این کاراکتر (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')
    

    [regex101@ تست آنلاین]

  • 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,')
    

    [regex101@ تست آنلاین]

  • s\

    معادل [t\n\r\f\v\ ]، به معنی عمل تطبیق با کاراکتر فضای خالی است.

    \s
    
          1237 Main Street,     => 2 Matched (2 spaces)
    

    [regex101@ تست آنلاین]

    البته باید توجه داشته که منظور از کاراکترهای 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,')
    

    [regex101@ تست آنلاین]

  • w\

    معادل [_a-zA-Z0-9]، به معنی عمل تطبیق با کاراکترهای الفبایی و عددی (زبان انگلیسی) به همراه کاراکتر _ یا underscore.

    \w{2,5}
    
          1237 Main Street,     => 3 Matched (at '1237', 'Main', 'Stree')
    

    [regex101@ تست آنلاین]

  • W\

    برعکس w\ - معادل [_a-zA-Z0-9^]، به معنی عمل تطبیق با کاراکتری به غیر از کاراکترهای الفبایی و عددی (زبان انگلیسی) به همراه کاراکتر _ یا underscore.

    \W
    
          1237 Main Street,     => 3 Matched (2 spaces and ',')
    

    [regex101@ تست آنلاین]

  • 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‌ها ابتدا کاراکتر(هایی) را تطبیق و سپس از آن(ها) گذر می‌کنند.



😊 امیدوارم مفید بوده باشه