۳ ریجکس مفید برای توسعه دهندگان وب | به زبان ساده

ما در مجله فرادرس پیشتر مقالات متعددی در مورد اهمیت و ماهیت ریجکس (Regex) به رشته تحریر درآوردهایم و کاربردهای آن را نیز با ارائه راهنماهای عملی مختلف بیان کردهایم. اما هر چه از اهمیت ریجکس گفته شود، کم است. در این مقاله به بررسی 3 ریجکس مفید برای توسعهدهندگان وب میپردازیم.
قبل از هر چیز باید توضیح بدهیم که همه مثالهای مطرح شده در این راهنما با استفاده ابزار آنلاین regexr (+) تست شدهاند. همچنین بهتر است آشنایی قبلی با موارد زیر داشته باشید:
- تعیین موقعیت ریجکس مانند ^ و $
- مجموعه کاراکترها مانند [a-z] و [A-Z]
- تعیین کمیت مانند *، + و?
- جایگزینی مانند {5} و {2,} {1,3}
- گروهها مانند s (?=abc)
- تغییراتی مانند ab | cd
با ما همراه باشید تا با 3 ریجکس مهم برای توسعهدهندگان وب آشنا شوید.
ریجکس تائید ایمیل
برای تائید صحت نشانی ایمیل میتوانید از این ریجکس استفاده کنید.
کدهای آن را نیز در ادامه آوردهایم.
([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})*$
کاربرد این ریجکس در کد به صورت زیر است:
const regex = new RegExp('^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})*$'); const test1 = 'glipmsum_1%-@gmail.com'; const test2 = '12glipms12um_1%-@outlook.com'; const test3 = '12glipms12um_1%-@gmail.co.ca'; const test4 = '12glipms12um_1%-@gmail'; const test5 = '12glipms12um_1%-@gmail.e'; const test6 = '12glipms12um_1%-@gmail.edu.'; console.log(regex.test(test1)); // true console.log(regex.test(test2)); // true console.log(regex.test(test3)); // true console.log(regex.test(test4)); // false console.log(regex.test(test5)); // false console.log(regex.test(test6)); // false
توضیح
در این ریجکس از دو کاراکتر anchor به صورت زیر استفاده شده است:
- علامت ^ برای نشان دادن ابتدای رشته.
- علامت $ برای نشان دادن موقعیت انتهای رشته.
برای تفهیم بیشتر به مثال زیر توجه کنید:
console.log(new RegExp('^abc$').test('abc')); // true console.log(new RegExp('^abc$').test('Abc')); // false // i : case insensitive // /^abc$/i/ console.log(new RegExp('^abc$', 'i').test('Abc')); // true
دیکشنری
- مجموعه کاراکتر [a-zA-Z0-9._%-] – در ریجکس فوق استفاده شده است که a-z با مجموعه کاراکترهای الفبایی از a تا z تطبیق مییابد و به کوچکی/بزرگی حروف حساس است. همچنین A-Z با کاراکترهای الفبایی از A تا Z تطبیق مییابد که باز حساس به کوچکی/بزرگی حروف است.
- در ادامه 0-9 آمده است که با ارقام 0 تا 9 تطبیق پیدا میکند.
- سپس کاراکتر (.) را میبینیم که با همان کاراکتر نقطه تطبیق پیدا میکند.
- در ادامه کاراکتر زیرخط (_) را شاهد هستیم که با خود این کاراکتر در رشته تطبیق پیدا میکند.
- کاراکتر % نیز با خود کاراکتر تطبیق مییابد.
- همچنین کاراکتر خط تیره (-) با خود کاراکتر تطبیق مییابد.
در مورد مجموعه کاراکترها باید به حالتهای زیر توجه کنید:
- [abc] – با هر یک از حروف a، b یا c تطبیق مییابد.
- [^abc] – با هر یک از حالتهایی که شامل a، b یا c نباشد تطبیق مییابد.
- [a-g] – با کاراکترهای بین a تا g تطبیق مییابد.
به مثال زیر توجه کنید:
const regex = new RegExp('[abc]'); const test1 = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry'; console.log(regex.test(test1)); // true (1 match) because there's "a" in "and" // // const regex = new RegExp('[^abc]'); const test1 = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry'; console.log(regex.test(test1)); // true (72 matches) every characters except for "a" in "and" // // const regex = new RegExp('[a-g]'); const test1 = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry'; console.log(regex.test(test1)); // true (12 matches) 12 matches for character "a","b","c","d","e","f","g" // // const regex = new RegExp('[^a-g]'); const test1 = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry'; console.log(regex.test(test1)); // true (61 matches) 61 matches for character NOT "a","b","c","d","e","f","g"
در ادامه ریجکسهای کمیتساز را بررسی میکنیم. در ریجکس فوق شاهد [a-zA-Z0-9._%-]+ هستیم.
- *a - نشانگر 0 یا چند مورد از توکن قبل خود است.
- -a - نشانگر 1 یا چند مورد از توکن قبل خود است.
به مثال زیر توجه کنید:
const regex = new RegExp('^(a*b*d*c*)$'); const test1 = 'aaaacccc'; const test2 = 'aaaabbddcccc'; const test3 = 'aaaabbcc'; const test4 = 'bbddcccc'; const test5 = 'cccc'; console.log(regex.test(test1)); // true console.log(regex.test(test2)); // true console.log(regex.test(test3)); // true console.log(regex.test(test4)); // true console.log(regex.test(test5)); // true // // const regex_plus = new RegExp('^(a+b+d+c+)$'); console.log(regex_plus.test(test1)); // false console.log(regex_plus.test(test2)); // true console.log(regex_plus.test(test3)); // false console.log(regex_plus.test(test4)); // false console.log(regex_plus.test(test5)); // false
در این بخش ریجکسهای مرتبط با جایگزینی را بررسی میکنیم. در ریجکس فوق شاهد توکنی به صورت a-zA-Z]{2,6} هستیم که توضیحات آن به صورت زیر است:
- a{5} – دقیقاً با 5 بار تطبیق مییابد.
- a{2,} – با 2 مورد یا بیشتر تطبیق مییابد.
- a{1,3} – بین 1 تا 3 بار تطبیق مییابد.
به مثال زیر توجه کنید:
const regex = new RegExp('a{5}'); const test1 = 'aaaaa'; const test2 = 'a'; console.log(regex.test(test1)); // true console.log(regex.test(test2)); // false // // const regex = new RegExp('a{3,}'); const test1 = 'aa'; const test2 = 'aaaa'; console.log(regex.test(test1)); // false console.log(regex.test(test2)); // true // // const regex = new RegExp('a{3,5}'); const test1 = 'aa'; const test2 = 'aaaa'; const test3 = 'aaaaa'; console.log(regex.test(test1)); // false console.log(regex.test(test2)); // true console.log(regex.test(test3)); // true
موارد دشوار
توجه کنید که برخی نشانیهای ایمیل نادرست پیچیده وجود دارند که ممکن است از سد اعتبارسنجی ریجکس فوق رد شوند. به مثال زیر توجه کنید:
12glipms12um_1%-@gmail.edu.df12glipms12um_1%-@gmail.edu
این مثال همان طور که در کد زیر میبینید از سد ریجکس اعتبارسنجی قبلی عبور میکند:
const regex = new RegExp('^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\. [a-zA-Z]{2,6})*$')
const regex = new RegExp('^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})*$'); const test1 = '12glipms12um_1%-@gmail.edu.df12glipms12um_1%-@gmail.edu'; const test2 = '12glipms12um_1%-@gmail.eduwerdsf'; // const test3 = 'aaaaa'; console.log(regex.test(test1)); // true console.log(regex.test(test2)); // false because it execeeds 6 letters after "."
ریجکس اعتبارسنجی پیشرفته ایمیل
برای این که بتوانیم نقیصه ریجکس قبلی را برطرف کنیم باید از یک کمیتساز (Quantifier) پیشرفته به صورت زیر استفاده کنیم:
a?- 0 یا 1
بدین ترتیب در صورتی که گروه () بیش از یک بار آمده باشد، این رشته از تست ما موفق بیرون نمیآید:
const regex = new RegExp('^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})?$'); const test1 = '12glipms12um_1%-@gmail.edu.df12glipms12um_1%-@gmail.edu'; console.log(regex.test(test1)); // false
پیچیدگی رمز عبور
در این بخش یک ریجکس مفید دیگر را برای توسعهدهندگان فرانتاند معرفی میکنیم. این ریجکس میزان پیچیدگی و قدرت یک رمز عبور را بررسی میکند:
(?=(.*[0-9]))(?=.*[!@#$%^&*()-_+=~`|:;\"'<>,./?])(?=.*[a-z])(?=(.*[A-Z]))(?=(.*)).{8,}
پیش از آن که به توضیح اجزای این ریجکس بپردازیم، باید اشاره کنیم که این ریجکس شامل تعداد زیادی «اطرافنگری» مثبت بودن به شکل?=(..) است. تعداد زیادی اطرافنگریهای مثبت و منفی وجود دارند که استفاده از هردوی آنها در حالتی که موقعیت حروف ثابت نباشد، مفید خواهد بود.
دیکشنری
توجه کنید که در مثال ریجکس قبلی برای اعتبارسنجی نشانی ایمیل ما موقعیت حروف را میدانستیم:
<email User Name> @ <email Provider>. <extension>
با این حال موقعیت حروف در رمزهای عبور ثابت نیست. به مثالهای زیر توجه کنید:
Gwl213ed!@ skwenfklEq!@!12
حالتهای ترکیب فراوانی برای رمزهای عبور وجود دارد و در واقع قاعده کار هم چنین است که رمزهای عبور قابلی پیشبینی نباشند. در نتیجه بهتر است در مواردی که موقعیت حروف قابل پیشبینی یا ثابت نیستند، بهتر است از بررسی مثبت بودن استفاده کنیم.
const regex = new RegExp("(?=(.*[0-9]))(?=.*[!@#$%^&*()-_+=~`|:;\"'<>,./?])(?=.*[a-z])(?=(.*[A-Z]))(?=(.*)).{8,}"); const test1 = 'HFNwdvwe513!'; const test2 = 'GGWNWND123!@@@!'; const test3 = 'GGWNWNmdD123!@@@!'; const test4 = 'GW1e!!'; console.log(regex.test(test1)); // true console.log(regex.test(test2)); // false NO lowercase letter console.log(regex.test(test3)); // true console.log(regex.test(test4)); // false Less than 8 letters
گروهها و پیشنگری مثبت
- (abc) – به دست آوردن گروه
- (?=abc) – بررسی مثبت بودن.
گروهها
به دست آوردن گروهها: گروهها ترکیبی از چند توکن هستند و ایجاد یک کپچر گروه برای استخراج زیررشته یا استفاده از ارجاع به قبل استفاده میشود:
const regex = new RegExp('^(abc)$'); const test1 = 'abc'; const test2 = 'abcd' console.log(regex.test(test1)); // true console.log(regex.test(test2)); // false // // const regex = new RegExp('^(ab*c)$'); const test1 = 'abc'; const test2 = 'ac' console.log(regex.test(test1)); // true console.log(regex.test(test2)); // true // * quantifier is for 0 or more
پیشنگری مثبت
در «پیشنگری مثبت» (Positive lookahead) یک گروه پس از عبارت اصلی بدون این که در نتیجه جای بگیرد، تطبیق مییابد. ساختار کار به صورت X(?=Y) است یعنی «به دنبال X بگرد، اما تنها در صورتی آن را تطبیق بده که پس از Y آمده باشد» یا به عبارت دیگر:
X(?=Y)(?=Z)
- X را پیدا کن.
- بررسی کن آیا Y درست پس از X آمده باشد (اگر چنین نبود، رد شو)
- بررسی کن آیا Z نیز دقیقاً پس از X آمده است (اگر چنین نبود، رد شو)
- اگر هر دو تست پاس شد، در این صورت X تطبیق یافته، در غیر این صورت به جستجو ادامه بده.
به مثال زیر توجه کنید:
const regex = new RegExp('(?=(.*[0-9])).'); const test1 = 'helloworld'; const test2 = 'helloworld1'; const test3 = '123'; console.log(regex.test(test1)); // false No number (0 - 9) console.log(regex.test(test2)); // true console.log(regex.test(test3)); // true
توجه کنید که خواندن از انتها سریعتر است. در ریجکس زیر نقطه (.) با هر کاراکتری به جز کاراکتر line break تطبیق پیدا میکند. با این فرض در رشته hel\nlo\n1 کاراکتر نقطه با شش کاراکتر عبارت به جز شکستگی خطوط تطبیق مییابد.
بدین ترتیب ریجکس فوق با هر عبارت کاراکتری (0 یا چند کاراکتر) که پس از آن عدد آمده باشد تطبیق مییابد و لذا helloworld1 و 123 تطبیق پیدا میکنند.
به عبارت دیگر میتوانیم ریجکس زیر را به چند «اطرافنگری مثبت» (positive lookaround) تجزیه کنیم:
(?=(.*[0-9]))(?=.*[!@#$%^&*()-_+=~`|:;\"'<>,./?])(?=.*[a-z])(?=(.*[A-Z]))(?=(.*)).{8,}
نتیجه کار به صورت زیر است:
(?=(.*[0-9])): looks for numbers (?=.*[!@#$%^&*()-_+=~`|:;\"'<>,./?]): looks for special characters (?=.*[a-z]): looks for lowercase letters (?=(.*[A-Z])): looks for upper case letters (?=(.*)): looks for everything
چنان که میبینید 5 گروه از اطرافنگری مثبت به صورت حریصانه (greedily) به دنبال تطبیقهای خود میگردند و تست ریجکس تنها در صورتی پاس میشود که همه معیارها تطبیق یابند.
در نهایت باید اشاره کنیم که در ریجکس فوق، در بخش.{8,} کاراکتر (0) تنها ریجکس خارج از اطرافنگری مثبت است و در واقع نقطه شروع واقعی ریجکس محسوب میشود. این توکن به دنبال هر کاراکتری به جز کاراکتر newline میگردد.
همچنین {8,} یک جایگزینی است که نماینده 8 حرف یا بیشتر است.
تاریخ در قالب YYYY-MM-dd
سومین و آخرین ریجکسی که در این مقاله معرفی میکنیم و برای توسعهدهندگان فرانتاند مفید و مهم است به صورت زیر است:
([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))
ریجکس فوق برای regexr نوشته شده است، اما برای این که در جاوا اسکریپت کار کند باید یک \ اضافی در ابتدای `\d` اضافه کنید، زیرا از آن به صورت یک رشته در RegExp در جاوا اسکریپت استفاده میکنیم:
([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))
به مثال زیر توجه کنید:
const regex = new RegExp('([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))'); const test1 = '1992-12-01'; const test2 = '2020-10-10'; const test3 = '3010-12-22'; const test4 = '2020-21-10'; const test5 = '3010-12-35'; console.log(regex.test(test1)); // true console.log(regex.test(test2)); // true console.log(regex.test(test3)); // false console.log(regex.test(test4)); // false console.log(regex.test(test5)); // false
دیکشنری
ابتدا شاهد یک توکن جایگزین به شکل ab | cd هستیم که با ab یا cd تطبیق مییابد. بنابراین ریجکس زیر را میتوان به سه بخش تجزیه کرد که با \ از هم جدا شدهاند:
([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))
نتیجه تجزیه به صورت زیر است:
([12]\\d{3}- (0[1-9]|1[0-2])- (0[1-9]|[12]\\d|3[01]))
این ریجکس شبیه یک دسته از گزارههای if است.
- در بخش اول شاهد توکن ([12]\\d{3} هستیم که مشخص میسازد حرف اول باید یکی از ارقام 1 یا 2 باشد [12] و سپس در ادامه باید سه رقم آمده باشد \\d{3}.
- در گزاره if دوم شاهد (0[1-9]|1[0-2]) هستیم که مشخص میکند این عبارت باید با 0 و یا 1 آغاز شود. اگر با 0 آغاز شود رقم بعدی باید درباره 1 تا 9 باشد، اما گر با 1 آغاز شود، در این صورت رقم بعدی باید در بازه 0 تا 2 باشد.
- گزاره if سوم به صورت (0[1-9]|[12]\\d|3[01])) است که مشخص میسازد عبارت باید با 0 آغاز شود یا با 1 یا 2 آغاز شود و یا با 3 آغاز شود.
- اگر عبارت با 0 آغاز شود، رقم بعدی باید در بازه 1 تا 9 باشد.
- اگر با 1 یا 2 آغاز شود، رقم بعدی میتواند هر رقمی `\d` [0-9] باشد.
- اگر با 3 آغاز شود، رقم بعدی باید 0 یا 1 باشد.
به این ترتیب به انتهای این مقاله میرسیم. امیدواریم این راهنمای کاربردی با عنوان 3 ریجکس مفید برای توسعهدهندگان وب مورد توجه شما قرار گرفته باشد.
سلام
عبارات با قاعده
در این خط کد بجای جایگزینی یک غلط میخواهم تمام کلماتی که در یک جمله غلط نوشته شده را اصلاح کنم اما در replaceفقط امکان تغییر یک کلمه وجود دارد .راه حل چیست؟let x=text.replace(“fisnsh”,”in”) مثلا
البته از روشهای دیگر نمی خواهم استفاده کنم.fisnshکلمه غلط است .finish صحیح آنست