مدونة عمار الخوالده

مقدمة في تعلم Regex


مقدمة في تعلم Regex

التعابير النمطية - Regular Expressions (Regex) هي نصوص تستخدم لوصف نصوص أخرى، فهي أشبه بقواعد لطريقة كتابة النص، يجب على سبيل المثال كتابة البريد الإلكتروني بصيغة محددة، هذه الصيغة يمكن وصفها باستخدام التعابير النمطية، كما يمكن استخدام التعبير النمطي داخل برامجنا للتحقق من مطابقة النصوص المدخلة الى النظام لصيغة محددة.

استخدامات Regex لا تقتصر على البرمجة فقط، كثير من البرامج كبعض محررات النصوص أو محررات الكود، تدعم البحث عن محتويات الملفات أو عن أسماء الملفات باستخدام أنماط Regex، كذلك بعض أدوات أنظمة التشغيل.

معظم تعابير Regex مدعومة في كل لغات البرمجة، قد توجد اختلافات طفيفة بينها، لكن الأساسيات واحدة.

تتم كتابة التعابير النمطية بين اشارتي /، في هذا المقال سأقوم بشرح التعابير النمطية من خلال الأمثلة، الأمثلة في البداية قد تكون بسيطة جدا، لكن مع التقدم في الأمثلة ستتضح الفوائد الخاصة بـ Regex.

المطابقة التامة - Exact Match

يمكن مطابقة نص محدد بالضبط دون اختلافات باستخدام Regex، فمثلا لو كانت لدي مجموعة من الأسماء التي تدخل إلى النظام، وأريد مطابقة إسم “ahmad”، ففي هذه الحالة سيكون التعبير النمطي المستخدم هو “ahmad”:

ammar ahmad ahmed test

في هذه الحالة البسيطة، يقوم البرنامج الذي ينفذ التعابير النمطية بالتحقق من الأحرف على الترتيب، ففي المدخل الأول، سيتم مطابقة حرف a في الاسم “ammar” لكن عند الوصول إلى حرف m سيجد أنه لا يطابق h في التعبير النمطي، فيعتبره غير مطابق للمطلوب.

هذا المثال لن يوضح الفائدة الفعلية من Regex لأنه بسيط جدا ويمكن الاستغناء عن Regex في هذه الحالة، لكن ستتضح الفائدة مع التقدم في الأمثلة.

لاحظ أن تعابير Regex تتم كتابتها بين اشارتي / أما بالنسبة للإشارة ^ فهي تعني بداية السطر، و $ تعني نهاية السطر، يمكن عدم استخدام ^ و $ في حال عدم احتياجهما، أما بالنسبة للأمثلة في هذا المقال فنحن بحاجة إلى مطابقة كل سطر بشكل مستقل، لذلك نقوم باستخدامهما.

المطابقة مع وجود حرف غير محدد

إشارة النقطة . تقوم بمطابقة أي حرف أو رمز، يمكن تعديل المثال السابق لمطابقة إسم أحمد سواء كتب ahmad أو ahmed عن طريق استخدام النقطة:

ammar ahmad ahmed test

في المثال السابق يوجد خطأ سيتم تحديده ومعالجته لاحقا خلال المقال، هل تستطيع تحديده قبل إكمال القراءة؟ :)

لكن ماذا لو احتوى النص على نقطة بالفعل وأردنا مطابقتها؟ استخدام علامة النقطة لمطابقة نقطة في النص لن يكون خيارا صحيحا، فهو سيطابق النقطة وغيرها:

ammar.dev ammar dev ammar0dev

لمطابقة النقطة يمكن استخدام رمز الـ Escaping \، هذا الرمز يستخدم في حال أردنا مطابقة إحدى الرموز المستخدمة في regex ومعاملته كنص عادي بدلا من معاملته كرمز خاص لـ regex.

جرب تعديل التعبير المنطقي السابق بإضافة رمز \ قبل النقطة وشاهد تغير النتيجة.

مطابقة عدة خيارات

في المثال الخاص بمطابقة ahmad و ahmed يوجد خطأ قد يؤدي إلى مشكلة في المدخلات، لاحظ المثال التالي:

ahmad ahmed ahm5d

لاحظ أن المطلوب هو كلمة ahmad او ahmed، لكن الرمز . يطابق أي حرف او رمز دون تحديد للعناصر المقبولة.

يمكن استخدام الأقواس المربعة [] لوضع عدة خيارات مقبولة للأحرف:

ahmad ahmed ahm5d

استخدام [ae] يجعل المطابقة تطابق أحد الحرفين a أو e فقط.

يمكن تحقيق نفس النتيجة باستخدام (a|e) باستخدام هذه الطريقة يجب اضافة الرمز | بين العناصر المطلوبة، لاحظ أيضا أننا استخدمنا الأقواس العادية بدل المربعة، هذه الطريقة تسمى الـ Alternation سيتم كتابة تفاصيل أكثر عنها عند الحديث عن المجموعات.

يمكنك إضافة العدد الذي تريده من الحروف والرموز داخل الأقواس المربعة، فمثلا لو كنت تتحقق من إدخال المستخدم لرقم، يمكن كتابة جميع الأرقام ضمن الأقواس [0123456789] لكن للتبسيط، يمكن اختصارها وكتابتها على هذا الشكل [0-9]، ويمكن استخدام نفس الطريقة مع الأحرف [a-z].

ملاحظة: الاختصار [a-z] سيعمل مع الأحرف الانجليزية في حالتها الصغيرة Small Letters، يمكن استخدام الاختصار [A-Z] للأحرف الكبيرة، كما يمكنك استخدام أكثر من اختصار معا [a-zA-Z0-9]

التكرار في التعابير

لو أردنا مطابقة رقم، فسنستخدم [0-9] لفعل ذلك، لكن المشكلة أن هذا الرقم سيتكون من خانة واحدة، الرقم 5 ستتم مطابقته بشكل صحيح، لكن الرقم 55 لن تتم مطابقته.

لحل هذه المشكلة نستخدم الرمز * لتكرار الرمز السابق:

5 87 100

اشارة * تعني تكرار العنصر أو عدم وجوده اطلاقا، فمثلا لو استخدمنا التعبير a[0-9]* فمن المسموح ادخال أي عدد من الأرقام، كما يُسمح بعدم ادخال أي. رقم:

a55 a

بينما اشارة + تشترط وجود العنصر لمرة واحدة على الأقل:

a55 a

التكرار بعدد محدود من الأرقام

إشارة * وإشارة + تقبلان التكرار بشكل لا نهائي، لكننا أحيانا نحتاج لتحديد عدد معين من الخانات:

5 87 100

كما يمكن تحديد مدى معين من الأرقام، كالسماح بالتكرار من مرتين الى 4 مرات:

5 87 100 12345

مجموعات المطابقة - Capturing Groups

عند وجود نص محدد، يمكن تقسيم التعبير النمطي إلى عدة تعابير أصغر، تقسيم التعابير النمطية يسهل التعامل معها برمجيا، بحيث يمكن الحصول على كل جزء من النص في Variable مختلف، كما يمكن استثناء بعض المجموعات وعدم مطابقتها أو إعطائها إسما يعبر عنها لتسهيل التعامل معها.

التعبير التالي يقوم بعمل مطابقة (Matching) للبريد الإلكتروني دون استخدام الـ Capturing Groups:

[a-zA-Z0-9]+@[0-9a-zA-Z\.]+

المثال للتوضيح فقط لذلك تم تبسيطه، البريد الالكتروني يمكن أن يحتوي على العديد من الرموز التي لا يدعمها التعبير النمطي السابق

عند تنفيذ هذا التعبير برمجيا، سيقوم بإعطائك نتيجة مطابقة (Match) واحدة في حال كتبت بريدا الكترونيا تنطبق عليه شروط التعبير، سنقوم باستخدام المجموعات للحصول على أكثر من Match، بحيث أحصل على اسم المستخدم مستقلا عن الـ Domain.

استخدام المجموعات بسيط جدا، عليك فقط إضافة التعبير النمطي بداخل الأقواس ()، المثال التالي يوضح استخدام مجموعات المطابقة على نفس التعبير المستخدم في المثال السابق:

عند استخدام التعبير السابق، سيتم الحصول على 3 نتائج، فمثلا لو قمت بادخال بريدي الالكتروني me@ammar.dev، ستكون النتيجة الأولى للمطابقة هي me@ammar.dev وهي تمثل النص الذي تمت مطابقته بالكامل (Full Match)، كما سيتم الحصول على نتيجة المجموعة الأولى me ثم نتيجة المجموعة الثانية ammar.dev، لاحظ أن رمز @ لم يكن ضمن أي مجموعة، لذلك لم يتم الحصول عليه إلا في نتيجة المطابقة الكاملة.

تسمية المجموعات

التعابير النمطية قد تكون كبيرة جدا ومعقدة أحيانا، كما سيكون من الصعب أحيانا الحصول على نتيجة المطابقة برمجيا بناء على ترتيب المجموعة ضمن النتائج.

يمكن إضافة اسم لكل مجموعة بحيث يعبر عنها بشكل واضح:

استخدام الـ Alternation مع المجموعات

المثال التالي فيه عدد من عناصر HTML:

1<strong>Strong Content</strong>
2<b>Bold Content</b>
3<div>Divider Content</div>

لنفترض أننا بحاجة للحصول على محتويات <strong> و <b> فقط، كيف نكتب تعبيرا للحصول عليها؟

في البداية، علينا مطابقة الـ HTML Tags، لاحظ أننا بحاجة إلى مطابقة strong أو b، في هذه الحالة سنستخدم رمز | ويسمى في Regex رمز الـ Alternation.

<strong>Strong Content</strong> <b>Bold Content</b> <div>Divider Content</div>

في هذا المثال، نحن مضطرون لاستخدام المجموعات مع رمز |، فاستخدام الـ Alternation دون مجموعة يعطي معنى مختلف، فالنمط <(strong|b)> يعني إما <strong> أو <b> بينما النمط <strong|b> يعني إما <strong أو b>.

استثناء مجموعة من النتائج - Non-Capturing Groups

قد تحتاج أحيانا إلى انشاء مجموعة دون اظهارها في قائمة المجموعات، هذا انوع من المجموعات يسمى (Non-Capturing Groups)، وهي مجموعة تبدأ بالرمز ?:، ففي المثال السابق كنا مجبرين على انشاء مجموعات للـ tags، لكننا لا نحتاج هذه المجموعات، وقد نرغب باستثناءها من النتيجة، المثال التالي يوضح طريقة الاستثناء:

<(?:strong|b)>(?<tag_content>.*)<\/(?:strong|b)>

المطابقة الاختيارية

يمكن استخدام علامة الاستفهام ? لتحديد النمط السابق على أنه اختياري:

https://ammar.dev http://ammar.dev ://ammar.dev

لاحظ أن حرف s هو الاختياري هنا وليس كلمة https، لأن علامة الـ ? تعمل على الحرف أو الرمز الذي يسبقها مباشرة فقط، وتعمل أيضا على المجموعات.

رموز التعديل/التغيير - Modifiers

الـ Modifiers في Regex أو (Regex Flags) هي رموز تكتب في نهاية التعبير النمطي (بعد إشارة / الأخيرة)، هذه الرموز قد تغير من طريقة تفسير لغة البرمجة للنمط.

قلنا سابقا أن هناك بعض الاختلافات الطفيفة أحيانا في تفسير Regex في لغات البرمجة، بحسب تجربتي، فمعظم الاختلافات هي في الـ Modifiers المدعومة.

الجدول التالي يوضح بعض الـ Modifiers:

الـ Modifier الوصف
i Case Insensitive Match - مطابقة دون اخذ حالة الاحرف الانجليزية بالاعتبار
g Global - بدون هذا الـ Modifier ستتوقف عملية الـ Matching عند أول نتيجة
x تدعم إضافة comments باستخدام اشارة # وتتجاهل المسافات

مثال على استخدام Regex في Javascript

يمكن استخدام Regex مع أي لغة برمجة، سنعطي بعض الأمثلة في هذا المقال باستخدام Javascript، توجد عدة methods للتعامل مع Regex في Javascript، منها. match() و replace().

1let pattern = /(?<username>[a-zA-Z0-9]+)@(?<domain>[0-9a-zA-Z\.]+)/
2let email = 'me@ammar.dev'
3
4email.match(pattern)

لاحظ أن الـ Expression يُعرف في Javascript بمجرد كتابته بين اشارتي / ولا يتم كتابته وكأنه String.

إذا احتجت أن تستخدم Expression موجود داخل String لأي سبب، يمكن استخدام RegExp Object في جافاسكربت.

وهذه نتيجة تنفيذ المثال السابق:

1[
2  'me@ammar.dev',
3  'me',
4  'ammar.dev',
5  index: 0,
6  input: 'me@ammar.dev',
7  groups: { username: 'me', domain: 'ammar.dev' }
8]

راجع التوثيق على Regular expressions - JavaScript | MDN للمزيد عن استخدام Regex في Javascript.

تعابير Regex واللغة العربية

قمنا في الأمثلة السابقة باستخدام مدى من الأحرف [a-z] أو الأرقام [0-9] لكن ماذا عن اللغة العربية؟ قبل عرض طريقة التعامل مع اللغة العربية في Regex، يجب أن نفهم كيف تعمل ميزة مدى الأحرف في Regex، هذا المدى يعمل عن طريق ترتيب الرموز المستخدمة في جداول ASCII أو Unicode، فالمدى التالي لا يعمل في Regex بشكل صحيح: [5-23] لأن تعامل Regex مع رمز ASCII وليس مع الأرقام نفسها.

الشيء نفسه ينطبق على اللغة العربية، اذا قمت باستخدام هذا التعبير [ا-ي] فبعض الحروف لن يشملها التعبير، مثل أ و آ و ء، راجع الجدول في هذه الصفحة على ويكيبيديا وستجد أن الحروف التي لم يشملها التعبير السابق لديها كود Unicode برقم أقل من حرف ا، بالتالي تحتاج لكتابة التعبير على شكل [ء-ي] لتشمل جميع الأحرف.

لكن الحركات والأرقام العربية مثل ١٢٣ لن تكون مشمولة أيضا بالتعبير السابق، يمكنك تعديل التعبير السابق بما يناسب استخدامك إن أردت أن تشمل الحركات أو الأرقام.

شخصيا أفضل استخدام أكواد Unicode مباشرة بدلا من استخدام الأحرف العربية، خاصة أن كتابة الحركات داخل تعابير Regex لن يكون واضحا.

استخدام Unicode قد يختلف بحسب اللغة أو الأداة المستخدمة لتنفيذ تعابير Regex، فمثلا لكتابة تعبير لمطابقة الحركات بحسب الجدول السابق على ويكيبيديا من تنوين الفتح إلى السكون، فسيتم كتابة التعبير بهذه الطريقة في جافاسكربت:

/[\u064B-\u0652]/

بينما في PHP مثلا، سيتم كتابته بهذه الطريقة:

/[\x{064B}-\x{0652}]/u

لاحظ اختلاف طريقة الكتابة، كما يجب في PHP إضافة modifier خاص في حال استخدام رموز الـ Unicode في تعابير Regex.

أدوات ومواقع مفيدة

  • موقع يتيح لك تجربة التعابير التي تكتبها بسهولة: https://regex101.com

  • موقع لتعلم Regex بطريقة سهلة وتفاعلية: https://regexone.com

  • موقع يحتوي على بعض التعابير الجاهزة، كما يقوم برسم أو عمل Visualization للتعابير التي تكتبها https://ihateregex.io

  • مرجع كبير يوثق ويشرح Regex بشكل مفصل: https://www.regular-expressions.info

  • فيديو يوضح طريقة عمل Regex (كيف تمت برمجة Regex) قد لا تحتاج لفهم طريقة عمل Regex من الداخل لاستخدامه، لكن في حال أصابك الفضول عن طريقة عمله، فهذا الفيديو مناسب جدا https://www.youtube.com/watch?v=528Jc3q86F8

اقرأ أيضا