۰۰۳ پایتون و ماژول هفته – متن – قسمت سوم re
مقدمه
سلام و عرض ادب! امیدوارم راست و ریست باشین. توی قسمت دوم ماژول textwrap رو بررسی کردیم. در این قسمت می خوایم یه ماژول خیلی مهم رو بررسی کنیم. مهم نیست که وب سرور طراحی می کنید یا ماشین لرنینگ کار می کنید! دونستن re برای همه برنامه نویسای پایتون مثل نون شب می مونه. بنابراین حتما یاد بگیریدش و حسابی تمرین کنید تا مسلط بشید بهش. re یا Regular Expressions که به فارسی بهش میگن عبارات باقاعده و تو زبان های دیگه معمولا بهش regex هم گفته میشه کاربردش اینه که میاد طبق یک الگو، توی متن جستجو می کنه و نمونه هایی که با اون الگو مطابقت دارند رو پیدا می کنه و حتی می تونه با چیز دیگه ای جابجا می کنه. برای اینکه بهتر درک کنید به چه دردی می خوره به این مثال دقت کنید:
توی نویسندگی معمولا توصیه میشه جمله هایی که می نویسید بیشتر از ۲۵ کلمه نباشه (به طور کلی). خب حالا یه نویسنده یه کتاب ۲۰۰ صفحه ای نوشته و نسخه PDF رو برای شما فرستاده. وظیفه شما اینه که ببینید چه جملاتی توی این کتاب هستن که بیشتر از ۲۵ کلمه دارند؟ اونارو پیدا کنید. خب حالا چجوری؟!!! با re و چند تا if-statement می تونیم به راحتی این کارو انجام بدیم.
حمایت از گربهعصبانی
اگه دوست داشتید کمی از عصبانیت من کم کنید، می تونید برام دونیت کنید.
ممنون از همه رفقایی که با حمایتهاشون به من انگیزه دادند تا بیشتر بنویسم.
ماژول re
طبق رسم و رسومات دیرینه مون، وارد مفسر پایتون میشیم و بعد از ایمپورت کردن re، یک عدد help ازش می گیریم که ببینیم اصن چن چندیم:
بعد از بررسی متوجه میشیم که این ماژول یکم از ماژول هایی که تا الان بررسی کردیم (یعنی string و textwrap) پر پیمون تره. به توضیحی که برام نوشته نگاه کنیم:
This module provides regular expression matching operations similar to those found in Perl. It supports both 8-bit and Unicode strings; both the pattern and the strings being processed can contain null bytes and characters outside the US ASCII range. Regular expressions can contain both special and ordinary characters. Most ordinary characters, like “A”, “a”, or “0”, are the simplest regular expressions; they simply match themselves. You can concatenate ordinary characters, so last matches the string ‘last’.
ترجمه:
این ماژول عملیات تطابق عبارات باقاعده را ارائه می دهد، مانند آن عملیات هایی که در زبان پرل یافت می شود. هم از رشته های ۸-بیتی پشتیبانی می کند و هم از یونیکد. هم الگو و هم رشته هایی که پردازش می شوند می توانند شامل بایت های نال و کاراکترهای خارج از محدوده US ASCII باشند. عبارات باقاعده می توانند هم شامل کاراکترهای خاص و هم ordinary باشند. اکثر کاراکترهای ordinary مثل “A”، “a” یا “0” ساده ترین عبارات باقاعده هستند. آنها با خودشان مطابقت دارند.شما می توانید کاراکترهای ordinary را اینطوری در نظر بگیرید که last با رشته ‘last’ مطابقت دارد.
منظور از کاراکترهای ordinary کاراکترهای ساده هستش. مگه کاراکتر غیرساده هم داریم؟ بله. در ادامه می بینیم. در واقع یه سری کاراکترها داریم که بهشون میگن special character یا کاراکترهای خاص. این کاراکترها معنایی فراتر از معنای اولیه شون دارن. مثلا “.” یعنی تطابق با هر کاراکتری به جز newline. یا مثلا “+” یعنی الگوی عبارت باقاعده قبلی حداقل یکبار تکرار شده باشد.
از اونجایی که تعداد این کاراکترهای خاص زیاده، من همشونو اینجا بررسی نمی کنم. پیشنهاد می کنم خودتون از روی مستندات کتابخونه re بخونید و استفاده کنید.
ماژول re این توابع رو در خودش جای داده:
- match
- fullmatch
- search
- sub
- subn
- split
- findall
- finditer
- compile
- purge
- escape
بعضی از این توابع، یکسری آرگومان های ورودی می گیرند که بهشون میگیم فلگ. این فلگ ها شامل موارد زیر هستند:
- A – ASCII
- I – IGNORECASE
- L – LOCALE
- M – MULTILINE
- S – DOTALL
- X – VERBOSE
- U – UNICODE
همونطور که تا اینجای کار دیدید، این کتابخونه خیلی جامع و کامل هست. حالا نگاهی به کلاس های موجود در این ماژول می کنیم:
- error
باورش سخته ولی فقط یک کلاس داره که اونم error هستش. این کلاس درصورتی که عبارت باقاعده ای که نوشته شده نامعتبر باشه، یک exception را raise میکنه (خدا خیرش بده).
توابع
با تابع match شروع می کنیم. تابع match یک عبارت باقاعده را با ابتدای یک رشته مطابقت می دهد:
همونطور که مشاهده می کنید، ورودی اول الگوی عبارت باقاعده هست و ورودی دوم رشته ای که می خوایم این الگو رو باهاش مطابقت بدیم. بعد از این کار مشاهده می کنیم که متغیر found یک Match object هستش. این نشون میده که تونسته این الگو رو با این رشته تطابق بده. دلیلش هم واضحه! رشته text با mad شروع میشه پس اینا با هم تطابق دارند.
چی شد؟ مگه ‘not’ توی text نیست؟ پس چرا چیزی پیدا نکرد؟ جواب اینه که تابع match الگو رو فقط و فقط با ابتدای رشته مطابقت میده و نه با کلش. پس با اینکه ‘not’ در این رشته هست ولی با match امکان تطبیق دادنش نیست. یادتون باشه اگه match چیزیو پیدا نکنه، None بر می گردونه.
تابع match یه آرگومان سومی هم میگیره که همون فلگ ها هستند. فعلا کاری با فلگ ها نداریم.
تابع fullmatch، یک الگوی عبارت با قاعده رو روی کل رشته مطابقت میده و نه فقط اولش! درصورتی Match Object بر می گردونه که عبارت با قاعده با کل رشته مطابقت داشته باشه. فرض کنید یک لیستی از شماره تلفن ها دادن که البته بعضی هاشون ساختار شماره تلفن های ایرانی رو ندارن. یعنی با ۰۹ شروع نمیشن. و ما قراره این ها رو تشخیص بدیم. ببینید:
اینکه قبل از رشته یک r قرار دادم به این معنی هستش که در این رشته، یکسری کاراکترها اونطوری که در رشته ها معنی دارند اینجا ندارند!! مثلا n\ صرفا یعنی n\ و معنی newline نمیده. اگر این r رو نمیزاشتم اونوقت تابع fullmatch واقعا دنبال اون رشته میگشت.
قسمت ۰۹ که ثابته و داره میگه حتما باید با ۰۹ شروع بشه.
قسمت d\ یعنی اینکه کاراکترهایی که بعد از ۰۹ میاد از جنس رقم هستند.
قسمت {9} داره میگه که بعد از ۰۹ باید ۹ رقم باشه.
مجموعه همه اینها میشه خروجی ای که مشاهده می کنید. شاید اگه تا الان با عبارات با قاعده کار نکرده باشید، با خودتون بگید که ای بابا اینم که سخته!!! راستش خیلی سخت نیست. کافیه که معنی همه چیز رو به صورت دقیق بدونید. اینجا حدودی دونستن کاری انجام نمیده و لازمه که معنی هرچیزی که ازش استفاده می کنید رو دقیق بدونید. این یک مثال خیلی ساده بود ولی در ابعاد بزرگتر ممکنه بی توجهی به این موضوع مشکل ساز بشه.
این مثال با match هم قابل حل شدنه! خودتون به عنوان تمرین امتحان کنید.
تابع search، یکی از توابع کاربردی ماژول re هستش. چون داخل رشته پیمایش می کنه و هر تطابقی که با الگوی عبارات باقاعده پیدا کنه بر می گردونه. در واقع دیگه محدودیت match یا fullmach رو نداره. سبک ورودی گرفتنش و خروجی دادنش دقیقا مثل دو تابع match و fullmatch هستش. یک مثال ببینیم:
همونطور که می بینید دنبال یک دنباله دو کاراکتری از حروف می گردیم که قبل و بعدش یک فاصله باشه. بنابراین رشته ‘so’ چیزیه که تابع search پیدا می کنه.
تابع sub، دو تا کار انجام میده. اول اینکه یک الگوی عبارت باقاعده رو با یک رشته مطابقت میده. دوم اینکه قسمت هایی از رشته که با این الگو مطابقت دارند رو با یک رشته دیگه جایگزین میکنه. ورودی های این تابع این شکلیه:
ورودی اول الگوی عبارت، ورودی دوم که هم می تونه یک رشته باشه و هم یک callable. ورودی سوم رشته ای هست که می خواهیم توش بگرده و الگو رو باهاش تطبیق بده. ورودی های بعدی هم اختیاری هستند که کاری باهاشون نداریم در اینجا.
شاید براتون سوال پیش بیاد که callable در پایتون چیه؟ به طور کلی در پایتون، به هر شئ که بتونه یکسری ورودی دریافت کنه و بتونه خروجی تولید کنه میگن callable. به عنوان مثل تابع و کلاس دو callable معروف در پایتون هستند.
این مثال رو دریابید. می خواهیم تک تک حروف این رشته رو با عبارت ‘so so’ جایگزین کنیم. به چه دردی می خوره؟ نمیدونم 😐
تابع subn، همون تابع sub هست. با این تفاوت که خروجی ای که میده یه رشته نیست. بلکه یه tuple هستش. این تاپل دو تا آیتم داره: رشته جدید (جایگزین شده) – تعداد جایگزینی ها. یا به عبارتی بهمون میگه که چند بار موفق شدم توی این رشته، اون الگو رو پیدا کنم و با رشته جدید جایگزینش کنم. مثال قبلیو با subn ببینیم:
تابع split، یک رشته رو با توجه به رخدادهای یک الگو در اون میاد و تکه تکه می کنه. خروجیش یک لیست هست که رشته رو بر اساس اون الگوی عبارت باقاعده تکه تکه در خودش داره. به ساختار ورودی گرفتنش توجه کنید:
ورودی maxsplit که اختیاری هم هست، در صورتی که مقداری بهش داده بهش (عدد صحیح)، حداکثر تا اون تعداد عملیات split رو انجام میده. مثال:
تابع findall، تمام مطابقت هایی که با هم overlap ندارند رو در یک رشته پیدا می کنه و نهایتا یک لیست بر می گردونه که شامل این مطابقت هاست. یک مثال ببینیم با هم:
توی این مثال یک رشته دارم که توی متغیر string ذخیره شده. حالا می خوام کلمه هایی که با h شروع میشن رو توی این رشته پیدا کنم. پس دست به کار میشم و اینطوری که در بالا می بینید برنامه می نویسم. همونطور که مشاهده می کنید، خروجی findall یک لیست هستش.
تابع finditer، تا حد زیادی به findall شباهت داره. با این تفاوت که لیست بر نمی گردونه. بلکه یه iterator بر می گردونه. این برای مواقعی خوبه که حجم خروجی خیلی بزرگه و نمی خواید حافظه زیاد مصرف بشه. برای همین یکی یکی می خونید و سپس از یَک یَکشون استفاده می کنیم. مثال ببینم:
همونطور که می بینید به راحتی می تونیم با متد جادویی __next__ توی این iterator جلو بریم. البته برای راحتی کار معمولا از loop استفاده می کنند. ولی من خواستم با عمق داستان انس بگیرین.
تابع compile، برای مواقعی استفاده میشه که می خواهید از یک regular expression pattern چند بار استفاده کنید. و نمی خواید هر باید بیاید کل عبارت باقاعده رو بنویسید. در واقع compile یک الگوی عبارت باقاعده می گیره و به عنوان خروجی یک pattern object بر می گردونه که می تونید از سایر توابع ماژول re روش استفاده کنید. مثالی ببینیم:
تابع purge، برای پاک کردن کش مربوط به عبارات باقاعده استفاده میشه. مگه regex هم کش داره؟ داستان از این قراره که وقتی شما عبارت های باقاعده ای رو بدون re.compile می نویسید این عبارت ها برای بار اول کامپایل میشن بعد کش میشن. اکثر مواقع شما نیاز ندارید که بخواید کش مربوط به re رو پاک کنید. به این دلیل که حجم object هایی که داری خیلی کوچیکه و آنچنان تاثیری روی عملکرد نداره. اما در کل بدونید که همچین تابعی هم هست و شاید یه روزی به کارتون اومد.
تابع escape، کارش اینه که همه کاراکترهای موجود در یک الگو رو اسکیپ می کنه مگر اینکه اون کاراکترها حروف ASCII یا اعداد یا ‘_’ باشه. تابع escape یک ورودی میگیره که اونم یک pattern هستش. در واقع کاری که انجام میده اینه که یک پالایش اولیه روی pattern انجام میده. مثالی ببینیم:
ادامه
خب خب خب. عزیزان تا اینجای راه با زیر و بم ماژول re آشنا شدین. اما آیا این مقاله برای اینکه بتونید هر کاری دوس دارید توی re انجام بدید کافیه؟ نع. چند تا نکته مونده که ترجیح می دهم خودتون بخونید و تمرین کنید. یادتون باشه نوشتن re اوایلش کاره سختیه ولی بعد کم کم دستتون راه میافته. این کتابخونه به شدت براتون مفید خواهد بود. راجع به Flag ها و Grouping در re مطالعه کنید. سعی کنید زیاد تمرین کنید. همانا که رستگاری از آن ماست.
سخن پایانی
خب عزیزان دلم، این سومین قسمت از سری مقالات “آموزش کتابخانه های استاندارد پایتون” بود که تقدیم حضورتون شد. امیدوارم از خوندنش لذت برده باشید. لطفا اگر حس کردید این مطالب می تونه برای کسی مفید باشه حتما بهش معرفی کنید. راستی ببخشید یکم دیر شد. زندگیه دیگه.. بازیای خاص خودشو داره.
** لطفا و حتما من رو دنبال کنید. خوشحال میشم. **
تقدیم به جامعه پایتون ایران… امضا: گربهی عصبانی.