عبارت های منظم در پایتون و ترمینال — راهنمای کاربردی
عبارتهای منظم (Regular Expressions) ابزاری قدرتمند هستند که اگر با طرز کار آنها آشنا باشید، کمک بسیار زیادی به شما میکنند. اما اگر با عبارتهای منظم آشنا نباشید، ممکن است در نظرتان شبیه به زبان خارجی بیاید که نیاز به ترجمه شدن دارد. در هر صورت، در این راهنما با مبانی عبارت های منظم در پایتون و ترمینال آشنا خواهید شد و شیوه استفاده از آنها را برای تسهیل کارهای روزمرهتان به عنوان یک برنامهنویس خواهید آموخت. در ادامه این مبانی را بیشتر توضیح داده و با ارائه مثالهای عملی از کد پایتون و همچنین محیط ترمینال، دانش کافی در مورد شیوه راهاندازی آن را برای کدنویسی روزمره به دست میآورید.
مبانی عبارت های منظم در پایتون و ترمینال
عبارتهای منظم روشی برای جستجو به دنبال الگوها در مجموعه دادهها هستند. توجه کنید که عبارت منظم، خود یک زبان برنامهنویسی محسوب نمیشود، بلکه اغلب زبانهای عمده یک موتور regex دارند که میتوان به صورت داخلی از آن استفاده کرد.
عبارت منظم به چه معنا است؟
عبارت منظم یک دنباله از کاراکترها است که یک الگوی جستجو را تعریف میکند. منظور از دنباله کاراکترها، ساختار regex است و اگر تا کنون آن را دیده باشید، میدانید که خواندن آن چه قدر متفاوت و درک آن نیز به مراتب دشوارتر است. به عنوان مثال به دنباله زیر توجه کنید:
\(?[0-9]{3}\)?[\-\s]?[0-9]{3}[\-\s]?[0-9]{4}$
دنباله فوق در نگاه نخست میتواند کاملاً گیجکننده باشد. اغلب افراد حتی نمیتوانند معنی آن را بفهمند. اما این صرفاً یک عبارت برای تطبیق شماره تلفن است. در ادامه این مقاله این عبارت را بیشتر بررسی خواهیم کرد، بنابراین اگر فعلاً آن را درک نمیکنید، نگران نباشید.
چه زمانی باید یا نباید از regex استفاده کرد؟
با استفاده از regex میتوانیم کارهای زیر را به صورت برنامهنویسیشده انجام دهیم.
- یافتن الگوها و تطبیق در سری دادهها – شما میتوانید دادههای را در هر فرمتی که باشند چه csv. و چه txt. جهت یافتن الگوهای مشخص جستجو کنید.
- اعتبارسنجی فرمها و ورودیها – این کار در فرانتاند و پیش از آن که یک فرم تحویل شود، انجام میپذیرد تا مطمئن شویم که پس از ارسال هیچ مشکلی در پشت صحنه به وجود نخواهد آمد.
- گردآوری اطلاعات از وب – اگر میخواهید برای نمونه اطلاعات همه تگهای <a><a/> یک صفحه وب را گردآوری کنید، ریجکس یکی از بهترین ابزارها برای این کار محسوب میشود.
محدودیتهای عبارتهای منظم چیست؟
- عبارتهای منظم زبان برنامهنویسی محسوب نمیشوند. آنها صرفاً ابزاری هستند که زبانهای عمده برای مقاصد خاص مورد استفاده قرار میدهند.
- خواندن عبارتهای منظم دشوار است. به طور خاص اگر با عبارتهای منظم آشنا نباشید، آنها را کاملاً بیگانه میپندارید و شاید تا مدتهای مدیدی تلاش کنید از آنها دوری کنید.
- نوشتن صحیح آنها دشوار است. حتی اگر دقیقاً بدانید میخواهید چه کار بکنید، کار با عبارتهای منظم در هر حال دشوار است. برخی اوقات باید بیش از چندین بار تلاش کنید تا متوجه شوید که چطور میتوانید مقصود خود را با استفاده از عبارتهای منظم پیادهسازی کنید.
انواع متفاوت عبارتهای منظم
در این زمان شما درکی مقدماتی از مفهوم عبارتهای منظم دارید و با شیوه بهرهگیری از آنها آشنا شدهاید. با این حال توانایی نوشتن آنها ابزاری ارزشمند است که باید داشته باشید. در ادامه با روش عملی بهکارگیری عبارتهای منظم آشنا خواهیم شد.
مجموعه مقدماتی و بسطیافته
ریجکسها یک مجموعه مقدماتی و یک مجموعه بسط یافته دارند. در این راهنما به توضیح مجموعه مقدماتی میپردازیم و صرفاً بخشهای خاصی از مجموعه بسطیافته را توضیح میدهیم. برای مشاهده فهرست جامعی از ریجکسهای موجود به مستندات عبارتهای منظم (+) برای زبان پایتون مراجعه کنید.
نمادهای مجموعه مقدماتی
- نقطه (.) – این یک نماد وایلدکارد است که جایگزین هر کاراکتری میشود.
- توان (^) – با ابتدای یک رشته تطبیق مییابد.
- دلار ($) – با انتهای رشته تطبیق مییابد.
- ستاره (*) – با صفر ابتدایی یک رشته به میزان صفر تا چند بار که باشد تطبیق مییابد.
- ممیز برعکس (\) - نماینده کاراکترهای خاص و کاراکترهای escape است.
- پرانتز (()) – عبارتها را با هم گروهبندی میکند.
- علامت سؤال (?) – صفر یا یک بار با عبارت ابتدای یک رشته تطبیق مییابد.
- s\ – یک s به صورت escape-شده است که نماینده فضای خالی است.
- لفظ درون براکت [wxyz] - نماینده یکی از کاراکترهای درون براکت است.
- بازه درون براکت [w-z] – نماینده یکی از کاراکترهای درون بازه (در این مثال یکی از w ،x ،y یا z است) که میتواند روی اعداد [10-100] نیز استفاده شود.
- منفی براکت [wxyz^] – نماینده هر کاراکتری است که در مجموعه تعیینشده نباشد (در این مثال همه کاراکترهای به جز w، x، y یا z) توجه کنید که علامت توان درون براکت معنایی متفاوت از معنای قبلی آن دارد.
- کلمه (w\) – با هر حرف، رقم یا زیرخط در کد ASCII تطبیق مییابد.
نمادهای مجموعه بسط یافته
- علامت جمع (+) - با عبارت ابتدایی به میزان یک یا چند بار که باشد تطبیق مییابد.
- نماد پایپ (|) – به عنوان یک عملگر مقایسهای «یا» استفاده میشود که در مثال زیر یا با عبارت 1 و یا عبارت 2 تطبیق مییابد: expression1|expression2.
- {n} – با n رخداد عبارت ابتدایی تطبیق مییابد و مشابه *, +, و ? است به جز این که میتوانید بیشینه تعداد رخدادها را برابر با n تعریف کنید.
مهمترین تفاوتی که وقتی از مجموعه مقدماتی در برابر مجموعه بسطیافته استفاده میکنید، باید به خاطر بسپارید این است که برای نمونه در ادامه grep در ترمینال در زمان استفاده از نمادهای بسطیافته باید یک E- بیاید:
grep -E ‘your-extended-regex’
تجزیه عبارت
اکنون عبارتی که در ابتدای این مقاله ارائه کردیم مجدداً بررسی میکنیم:
\(?[0-9]{3}\)?[\-\s]?[0-9]{3}[\-\s]?[0-9]{4}$
برای این که این عبارت آسانتر خوانده شود، آن را به دستههای کوچکتر تجزیه کرده و از مهندسی معکوس برای نمایش طرز کار عملی آن برای تطبیق با یک شماره تلفن استفاده میکنیم. از نمادهای دارای برچسب فوق به عنوان یک راهنما استفاده میکنیم.
- - این نماد ابتدای یک رشته را مشخص میسازد و مفهوم سادهای دارد.
- \(? – پرانتز باز اختیاری – ابتدا آن را escape میکنیم (\) تا علامت پرانتز باز به عنوان یک کاراکتر لفظی خوانده شود. سپس یک علامت سؤال اضافه کردهایم تا صفر یا یک بار به دنبال این کاراکتر بگردد و بدین ترتیب اختیاری میشود.
- [0–9]{3} – این بخش به دنبال سه عدد پشت سرهم میگردد که در عمل یک کد استان را تشکیل میدهد (021 یا 041).
- \)? – پرانتز بسته اختیاری است.
- در این زمان، عبارت مورد نظر ما با یکی از عبارتهای 123 یا (123) تطبیق مییابد.
- ?[\-\s] – نماینده یک فاصله یا تیره اختیاری است. \S عبارتی برای فاصله یا فاصله خالی است که درون براکت با استفاده از یک \- یعنی تیره escape-شده و در ادامه با یک? آمده است تا صفر یا یک بار به دنبال آن بگردد.
- [0-9]{3} – به دنبال هر نوع سه رقم پشت سرهم میگردد.
- [\-\s]? – تیره یا فاصله اختیاری است.
- [0-9]{4} – عبارت نهایی پیش از تگ پایانی است که به دنبال هر چهار رقم پشت سرهم میگردد.
- $ -- انتهای رشته را نشان میدهد.
در این مثال هم ^ و هم $ را میتوان نادیده گرفت و استفاده از آنها برای این عبارت خاص الزامی ندارد. دلیل این که آنها را آوردهایم این است که دانستن معنایشان و آشنایی با طرز کارشان در این سناریو مفید است. اگر شماره تلفن در میانه متن دیگری بود، میتوانید هر دو آنها را حفظ کنید.
چنان که دیدید وقتی عبارت منظم فوق را تجزیه میکنیم، درک معنای آن چندان دشوار نیست. این عبارت با هر یک از قابلهای شماره تلفن زیر تطبیق مییابد:
- 1234567891
- 123 456 7891
- 123 456–7891
- 123–456–7891
- (123) 456 7891
- (123) 456–7891
اگر بخواهیم بخشهای خاصی از عبارت منظم را نادیده بگیریم، میتوانیم آن را سادهتر هم بنویسیم:
- [0-9]{10} – احتمالاً سادهترین روش برای بررسی است، چون در صورتی که هر نوع خط تیره یا پرانتز در متن وجود داشته باشد، تطبیق نخواهد یافت. این عبارت تنها به دنبال ارقام متوالی میگردد.
- [0-9]{3}[0-9]{3}[0-9]{4} – یک روش متفاوت برای نوشتن یک عبارت عددی با ده رقم متوالی است. این عبارت در واقع معادل عبارت فوق است و افزودن خط تیره یا پرانتز را آسانتر میسازد.
- [0-9]{3}[\-\s]?[0-9]{3}[\-\s]?[0-9]{4} – همانند قبلی است و تنها خطوط تیره و فواصل را بررسی میکند.
در بخش بعدی به بررسی روشی برای شناسایی الگوها و ساختن ریجکسهای خاص میپردازیم.
عبارت منظم برای تطبیق با نشانی ایمیل
در این زمان درکی مقدماتی از برخی از نمادها و ابزارها برای استفاده در زمان ساخت این عبارتها یافتهایم. در ادامه به بررسی ابرارهای موجود برای ساخت این عبارتها میپردازیم. همچنین یک الگوی دیگر که کاربرد زیادی دارد یعنی نشانی ایمیل را نیز بررسی میکنیم.
پیش از آغاز عملی ساخت ریجکس ابتدا باید با یک فرایند سه مرحلهای آشنا شویم که به شناسایی الگویی که میخواهیم تطبیق بدهیم کمک میکند:
- چه چیزی باید وجود داشته یا نداشته باشد؟
- یافتن الگوها در موارد مشمول و غیر مشمول.
- نوشتن آن الگوها در ریجکس.
به عنوان مثال، نشانیهای ایمیل زیر را در نظر بگیرید:
regular-expression-master101@gmail.com joe_smith@aol.com business-man99@bigbusiness.net
اکنون فرایند سه مرحلهای فوق را روی نشانیهای ایمیل اعمال میکنیم.
چه چیزی باید وجود داشته یا نداشته باشد؟
ابتدا ایمیلها را به بخشهای مختلف تجزیه میکنیم:
<username>@<second-level-domain>.<top-level-domain>
منظور دامنه سطح بالا (top-level domain) بخش انتهایی ایمیل است که معمولاً شامل عباراتی مانند .com،.net،.org و غیره است. در این مثال قصد داریم بیشینه طول این بخش را چهار کاراکتر (مانند .info) در نظر بگیریم.
یافتن الگوها در موارد مشمول یا غیرمشمول
نامهای کاربری هر چیزی میتواند باشند، اما امکان استفاده از کاراکترهای خاص به جز زیرخط، خط تیره و نقطه وجود ندارد.
- یک نماد @ به صورت ثابت در هر نشانی ایمیل وجود دارد.
- دامنه سطح دوم نمیتواند شامل کاراکترهای خاص به جز زیرخط، تیره و نقطه باشد.
- دامنه سطح بالا تنها میتواند شامل حرف باشد و طول بیشینه آن چهار و کمینه آن دو است.
نوشتن الگوها در یک ریجکس
- ریجکس برای نام کاربری به صورت [\w\-\.]+ است. \w با هر کاراکتر ASCII، رقم و خط تیره تطبیق مییابد. .\ یک نقطه است. همه این موارد درون یک براکت قرار گرفتهاند تا با هر یک از آنها تطبیق یابند و یک علامت + پس از آن نیز اضافه شده است تا یک یا چند بار بتوانند تطبیق پیدا کنند.
- نماد ثابت @ با کاراکتر @ و نه هیچ چیز دیگر تطبیق مییابد. این امر الزامی است.
- ریجکس مربوط به دامنه سطح دوم به صورت ([\w\-]+\.)+ است که مشابه عبارت نام کاربری است به جز این که امکان وجود چند عبارت پشت سر هم وجود دارد که با استفاده از پرانتز مشخص شده است (example@myurl.co.uk).
- ریجکس دامنه سطح بالا به صورت [a-zA-Z]{2,4} است که امکان وجود کاراکترها از a تا z را چه به صورت حروف کوچک و چه بزرگ فراهم میسازد و از دو تا چهار کاراکتر میتواند طول داشته باشد.
اگر همه این موارد را کنار هم قرار دهیم و یک علامت توان به ابتدای آن و علامت دلار به انتهایش اضافه کنیم، به ریجکس زیر دست پیدا میکنیم:
[\w\-\.]+@([\w\-]+\.)+[a-zA-Z]{2,4}$
اکنون میتوانید آن را به RegExr ارسال و روی تکتک ایمیلها تست نمایید و یا علامت توان و دلار را از آن حذف کرده و ایمیلها را به صورت گروهی تست کنید. پیشنهاد میکنیم بخشهای مختلفی را به ریجکس اضافه یا حذف کنید تا انعطافپذیری آن را افزایش دهید.
بدین ترتیب ما تا به اینجا با روش نوشتن ریجکسها آشنا شدیم. نوشتن ریجکسها کاری کاملاً عالی است اما روش استفاده عملی از آنها در برنامهنویسی روزمره چگونه است؟ در ادامه با روش استفاده از ریجکسها در ترمینال و در زبان پایتون آشنا خواهیم شد.
توجه کنید که موتورهای مختلف ریجکس این عبارتها را به روشهای متفاوتی مدیریت میکنند. برخی اوقات آنچه که روی وبسایت regexr.com یا در grep کار میکند، ممکن است در پایتون عمل نکند و یا برعکس. ممکن است لازم باشد ریجکس خود را با چند موتور مختلف امتحان کنید تا از کارکرد آن در همه محیطها مطمئن شوید.
عبارت منظم در ترمینال
در این بخش مثالی را با استفاده از grep بررسی میکنیم. grep یک ابزار خط فرمان است که برای جستجوی مجموعه دادههای متنی ساده استفاده میشود و برای خود موتور داخلی خاصی دارد. در این بخش شمارههای تلفن و نشانیهای ایمیلی که تا اینجا استفاده کردیم را یکجا گرد هم میآوریم و آنها در یک فایل txt. قرار میدهیم تا روی آن جستجو کنیم. متن زیر را کپی کرده و در پنجره ترمینال paste کنید:
1cat << EOF >> regex-test.txt
21234567891
3123 456 7891
4123 456-7891
5123-456-7891
6(123) 456 7891
7(123) 456-7891
8regular-expression-master101@gmail.com
9joe_smith@aol.com
10business-man99@bigbusiness.net
11EOF
به این ترتیب به فایل جدید به نام regex-test.txt ایجاد میشود و شمارههای تلفن و ایمیلها درون آن قرار میگیرند. در اینجا میتوانیم از grep برای جستجو موارد تطبیقی استفاده کنیم.
با توجه نسخه grep ما که 2.5.1 است، \s همواره برای شناسایی فواصل خالی کار نمیکند. به همین جهت، ممکن است لازم باشد وهلههای \s را با [:space:] و یا با کاراکتر واقعی فاصله عوض کنید. این نکته را به خاطر داشته باشید تا یک عبارت منظم برای استفاده با grep برای جستجوی شمارههای تلفن بسازیم. توجه داشته باشید که ما \s را با [:space:] عوض کردهایم و ^ و $ را نیز حذف میکنیم.
بدین ترتیب ریجکس شمارههای تلفن با grep به صورت زیر است:
\(?[0-9]{3}\)?[-[:space:]]?[0-9]{3}[-[:space:]]?[0-9]{4}
اگر بخواهیم جمعبندی بکنیم، با اجرای دستور فوق در همان دایرکتوری که فایل regex-test.txt قرار دارد. نتیجه زیر به دست میآید. اگر ریجکس کار نکرد به جای \s از کاراکتر فاصله واقعی استفاده کنید:
grep -E '\(?[0-9]{3}\)?[-[:space:]]?[0-9]{3}[-[:space:]]?[0-9]{4}' regex-test.txt
عبارت منظم در پایتون
یک تفاوت کلیدی که در زمان اجرای ریجکس در پایتون با آن مواجه میشویم، این است که به جای بررسی کل فایل غالباً بهتر است که هر بار یک مدخل را بررسی کنیم.
در این مثال ایمیلها را به یک لیست اضافه میکنیم و از یک حلقه برای بررسی تکتک آنها استفاده میکنیم. اگر ایمیلی تطبیق پیدا کند، ()match.group را پرینت میکنیم و اگر چنین نباشد، جمله Invalid Email را پرینت میکنیم.
ریجکس ایمیل دقیقاً همانند مثال قبلی است:
1In [1]: import re
2
3In [2]: regex = '[\w\-\.]+@([\w\-]+\.)+[a-zA-Z]{2,4}'
4
5In [3]: emails = ['regular-expression-master101@gmail.com', 'joe_smith@aol.com', 'business-man99@bigbusiness.net', 'my_email@aol.co.uk', 'my_email-123.com']
6
7In [4]: def valid_email(emails):
8 ...: for email in emails:
9 ...: match = re.search(regex, email)
10 ...: if match:
11 ...: print(match.group())
12 ...: else:
13 ...: print("Invaid Email")
14
15In [5]: valid_email(emails)
16regular-expression-master101@gmail.com
17joe_smith@aol.com
18business-man99@bigbusiness.net
19my_email@aol.co.uk
20Invaid Email
سخن پایانی
بدین ترتیب به پایان این مقاله با موضوع عبارتهای منظم در ترمینال و پایتون میرسیم. در این راهنما ابتدا مروری بر مفاهیم مقدماتی عبارتهای منظم داشتیم و سپس با روش بهکارگیری آنها در محیط ترمینال و زبان پایتون آشنا شدیم. با استفاده از این دانش شما میتوانید از قدرت ریجکسها بهره گرفته و بر کارایی برنامهنویسی خود به میزان قابل توجهی بیفزایید.