ما هي التعبيرات المنتظمة ؟
So What's A $#!%% Regular Expression,
Anyway?!
التعبيرات المنتظمة Regular Expressions (كذلك تعرف ب Regex) هي أداة في غاية القوة و الفعالية ، تستخدم في مقارنة القوالب و البدلات . هذه التعبيرات ذات شهرة كبيرة في عالم البرمجة . تقريباً متكاملة مع كل الأدوات المعتمدة علي نظام التشغيل يونيكس UNIX مثل المحرر vi و لغات البرمجة بيرل و PHP و برامج shell الخاصة بيونيكس مثل awk و sed . و سوف تجدها أيضاً في لغات النصوص البرمجية المعتمدة على العميل و ليس على الخادم ، مثل جافا سكريبت JavaScript . تماماً مثل (مادونا) شهرتها طبقت الآفاق عبر الحدود و البلدان بلا استثناء .
التعبير المنتظم يجعلك تبني قوالب باستخدام مجموعة من الرموز الخاصة ، هذه القوالب يمكن بعد ذلك مقارنتها بنص في ملف أو معلومات أدخلها مستخدم في نموذج على صفحة ويب على الإنترنت ، و إذا كان هناك تطابق أو لم يكن فإن الفعل المناسب سوف يتخذ و الجزء الصحيح من الشفرة سيتم تنفيذه .
على سبيل المثال ، أحد أكثر التطبيقات شيوعاً للتعبيرات المنتظمة هو التأكد إذا كان المستخدم قد أدخل عنوان بريده الإلكتروني بالصيغة الصحيحة في نموذج form على الشبكة أم لا . إذا كان في صيغة صحيحة فإن النموذج يتم معالجته كالمعتاد ، أما إذا لم يكن كذلك فسوف تظهر رسالة خطأ تنبه المستخدم إلى تصحيح الصيغة التي كتبها . و هكذا فإن التعبيرات المنتظمة تلعب دوراً هاماً في عمليات اتخاذ القرار الروتينية على الويب كل يوم ، و كذلك فإنها يمكن أن تستخدم بتأثير كبير في عمليات البحث و الاستبدال المعقدة .
التعبير المنتظم عادةً يبدو مثل هذا :
/love/
و ما يفعله هذا هو أن يطابق القالب love على كل ما ينطبق عليه في النص المراد بحثه . مثله مثل الكثير من الأشياء في الحياة ، من الأسهل أن تقارن على قالب محدد من أن تقارن على مفهوم ما . ما رأيك في شيء أكثر تعقيداً ؟ جرب هذا :
/fo+/
هذا القالب سوف ينطبق على كل الكلمات التي تبدأ بحرفي fo . علامة + التي رأيتها هي الأولى في ما نطلق عليه الرموز المتغيرة أو المتحولة Meta-Characters ، و هذه الرموز لها معنى خاص عندما تستخدم في قالب أو نموذج . العلامة + استخدمت لتطابق حدوث واحد أو أكثر من الحرف الذي يسبقها . بالنسبة لمثالنا فإن حرف f متبوع بحدوث واحد أو أكثر من حرف o .
كذلك بالإضافة للرمز المتغير + ، يوجد لدينا العلامة * التي تستخدم لتطابق حدوث صفر أو أكثر من الحرف السابق . و العلامة ؟ التي تستخدم لتطابق حدوث صفر أو واحد فقط من الحرف السابق .
(حدوث صفر أي عدم وجود الحرف السابق على الإطلاق) أي أن العلامة * تعني أنه يمكن عدم وجود الحرف السابق في الكلمة المطابقة ، كما أنه يمكن أن يوجد أكثر من مرة واحدة في الكلمة . أما العلامة ؟ فهي تعني أنه يمكن ألا يكون الحرف في الكلمة المطابقة و يمكن أن يوجد مرة واحدة فقط ، فإذا تكرر أكثر من مرة فإن الكلمة تعد غير مطابقة .
و هكذا فإن :
/eg*/
يمكن أن تطابق الكلمات : easy و egg و egypt .
بينما :
/wil?/
يمكن أن تطابق الكلمات : winnie و Wimpy و Wilson .
و في حالة إذا ما بدا لك كل هذا فيه شيء من عدم الدقة ، فإنه يمكنك أن تحدد مدى عدد مرات حدوث الحرف في الكلمة ، على سبيل المثال :
/jim{2,6}/
تحدد أن الحرف m يمكن أن يحدث كحد أدنى مرتين و كحد أعلى 6 مرات ، و بالتالي فإن هذا التعبير المنتظم يمكن أن ينطبق على : Jimmy و Jimmmmmy و لكن ليس Jim . و يمكنك أن تترك الحد الأعلى و لا تكتب رقم ليكون حداً مفتوحاً يطابق أي عدد من مرات الحدوث .
يمكنك أن تبحث بالتعبيرات المنتظمة عن مسافة بيضاء أو أرقام أو حروف أبجدية . و إليك هذه المجموعة المرحة من الرموز المتغيرة التي سوف تساعدك على فعل ذلك :
s\ تستخدم لمطابقة مسافة بيضاء واحدة ، بما فيها مسافات مفتاح tab و رمز السطر الجديد .
S\ تستخدم لمطابقة أي شيء ليس مسافة بيضاء .
d\ تستخدم لمطابقة الأرقام من صفر إلى 9 .
w\ تستخدم لمطابقة الحروف و الأرقام و الحروف ذات الخط السفلي .
W\ تستخدم لمطابقة أي شيء لا يندرج تحت w\ .
. تستخدم لمطابقة أي شيء ما عدا رمز السطر الجديد .
الآن ، ربما تفكر .. هذا شيء عظيم ، ولكن ماذا يعني كل هذا ؟
حسناً ، افترض أنك تريد أن تجد كل المسافات البيضاء في صفحة ما . يمكنك أن تكتب :
/\s+/
هذا سهل أليس كذلك ؟ و إذا كنت تبحث فقط عن الأرقام ، يمكنك أن تكتب :
/\d/
افترض أنك أمامك جدول حسابات مالية معقدة و تريد أن تجد بسرعة كل المبالغ المالية التي تبدأ من ألف جنيه فأكثر ، يمكنك أن تكتب :
/\d000/
هل تريد تضييق مجال البحث إلى البحث في بداية أو نهاية السلسلة فقط ؟ لهذا يوجد لدينا ما يسمى بمثبتات القوالب Pattern anchors و هي تقيد تعبيرك المنتظم إلى البحث في إما أول أو آخر حرف في السلسلة . و تصبح مفيدة جداً عندما تبحث عن طريقة لترشيح أو فلترة العديد من المطابقات المتداخلة .
يوجد مثبتا قوالب أساسيان ، الأول يمثله الرمز ^ (يمكنك أن تجده في الكيبورد مع الرقم 6) و هو يبين أن التعبير يجب أن يتم مطابقته فقط على بداية السلسلة المراد بحثها . على سبيل المثال :
/^hell/
سوف تتطابق فقط مع الكلمات التي تبدأ ب hell مثل hello و hellhound و لكن ليس shell مثلاً .
و كذلك لمطابقة نهاية السلسلة فقط يستخدم مثبت القوالب $ ، مثل :
/ar$/
سوف تطابق scar و car و bar و لكن ليس art أو army أو arrow .
هناك أيضاً طريقة أسهل لإضافة مثبت قوالب لتعبيرك ، و هو الرمز المتغير b\ . و هو يستخدم لمطابقة أي من حدي السلسلة سواء الحد الأول أو الأخير و ذلك حسب وضعه في التعبير . على سبيل المثال :
/\bbom/
سوف تطابق الكلمات التي تبدأ ب bom مثل bombay و bombshell . بينما :
/man\b/
سوف تطابق الكلمات التي تنتهي ب man مثل human أو woman .
على العكس من b\ يأتي الرمز المتغير B\ الذي يطابق أي شيء في السلسلة ما عدا حدودها .
إذا كان يمكنك أن تحدد مدى لعدد الحروف التي يمكن أن تتكرر في الكلمة المطابقة ، يمكنك أن تحدد نطاق الحروف التي يمكن أن تحدث في الكلمة نفسها . مثلاً :
/[A-Z]/
سوف تحدد نطاق الحروف التي يمكن أن تحدث في الكلمة بكل الحروف الأبجدية الكبيرة بدءاً من الحرف A إلى الحرف Z . أما التعبير :
/[a-z]/
سوف يطابق كل الحروف الأبجدية الصغيرة . كذلك :
/[0-9]/
سوف يطابق كل الأرقام من صفر إلى 9 .
و هكذا باستخدام هذه النطاقات التي يمكنك تحديدها ، يصبح من السهل عليك أن تبتكر تعبير منتظم ليطابق حقلاً يتكون من حروف أبجدية و أرقام معاً :
/([a-z][A-Z][0-9])+/
هذه تستخدم لتطابق كلمة تتكون من حروف و أرقام بطبيعتها مثل aB0 و ليس abc . بخلاف ما قد تعتقد فإن الأقواس العادية ( ) ليست موجودة فقط لتعذيبك ، فهي ملائمة تماماً للاستعمال إذا كنت تجمع معاً عدة أقسام في تعبير واحد . كذلك من الممكن أن توضع بدلاً من كل هذه الأقواس علامة الشرطة الأفقية | (يمكنك أن تجدها بجوار زر علامة = في الكيبورد) ، و تستخدم في الفصل بين عدة اختيارات في التعبير الواحد ، مثلاً :
/to|too|2/
و تعني مطابقة أي من الاختيارات الثلاثة to أو too أو 2 ، و كما ترى فإن هذه العلامة تصبح مفيدة جداً إذا كان لديك العديد من الاختيارات التي تريد مطابقتها .
يمكنك أيضاً أن تعكس المعنى الاعتيادي للتعبير المنتظم بمشغل النفي negation operator الذي يمثله أيضاً العلامة ^ ، على سبيل المثال :
/[^A-C]/
سوف تطابق أي شيء ما عدا تلك التي حددت نطاقها أي الحروف من A إلى C . لاحظ أن استخدام العلامة ^ يختلف خارج الأقواس المربعة عن استخدامها داخلها . فداخل الأقواس المربعة ، عندما وضعنا العلامة ^ استخدمناها في النفي ، لكن عند استخدامها خارج الأقواس فهي للمطابقة في أول سلسلة ما .
هذا جيد .. و لكن ماذا إذا كنت تريد البحث عن أحد الرموز المتغيرة Meta-characters في سلسلة البحث ؟ حسناً .. يمكنك أن تهرب من هذا ببساطة بأن تضع قبلها شرطة مائلة خلفية \ ليعاملها التعبير كعلامة عادية و ليس كرمز متغير له معنى خاص عنده . مثلاً :
/th\*/
سوف تطابق *th و ليس the مثلاً لأن رمز الهروب \ يتأكد من أن التعبير يعامل العلامة كحرف عادي و ليس كرمز متغير .
كل هذا الوقت كنا نتكلم عن التعبيرات المنتظمة ككل و ليس عنها في بيرل فقط . الآن سوف نتكلم عنها في بيرل . أمر مطابقة القوالب pattern-matching في بيرل عادةً يبدو بهذا الشكل :
operator/regular-expression/string-to-replace/modifiers
أو
المقيِّدات أو المعدِّلات/سلسلة-مراد-استبدالها/تعبير-منتظم/مشغل
المشغل يمكن أن يكون إما m أو s بناءاً على غرض التعبير المنتظم . m تستخدم لعمليات المطابقة فقط (match) ، بينما s تستخدم لعمليات الاستبدال (substitution) .
التعبير المنتظم هو القالب الذي يتم المطابقة عليه ، هذا القالب يمكن بناؤه باستخدام مجموعة متنوعة من الرموز و الرموز المتغيرة و مثبتات القوالب .
السلسلة المراد استبدالها هي السلسلة التي يتم استبدالها عن طريق عمليات البحث و الاستبدال .
المقيِّدات أو المعدِّلات تستخدم لتتحكم في الطريقة التي يتم بها تطبيق التعبير المنتظم .
إذن الجملة :
s/love/foolery/
سوف تستبدل أول حدوث لكلمة love بكلمة foolery . و إذا أردت أن تحدث عمليات بحث و استبدال شاملة global ، يجب عليك أن تستخدم المقيد g في نهاية الجملة ، لتصبح :
s/love/foolery/g
إذا كنت تريد أن تطابق كلمات بدون مراعاة لحالة الحروف إذا كانت كبيرة أو صغيرة ، يمكنك أن تستخدم المقيد i في نهاية الجملة كما في :
m/jewEL/i
فهذا يطابق الكلمات jewel و Jewel و JEWEL .
في بيرل كل عمليات التعبيرات المنتظمة تتم عبر مشغل المساواة equality operator و المتمثل في ~= و المثال التالي يوضح استخدامه :
$flag =~ m/abc/
هذه الجملة تعني أن المتغير flag$ سوف تكون قيمته صحيحة true إذا كان يحتوي على abc . أما :
$flag =~ s/abc/ABC/
فسوف يغير كل حدوث ل abc في المتغير flag$ إلى ABC .
إليك هذا البرنامج البسيط الذي يسألك عن عنوان بريدك الإلكتروني و يقارنه بتعبير منتظم ليتأكد إذا كان في الصيغة الصحيحة أم لا :
#!/usr/bin/perl
# get input
print "So what's your email address, anyway?\n";
$email = <STDIN>;
chomp($email);
# match and display result
if ($email =~ /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/)
{
print "Ummmmm....that sounds good!\n";
}
else
{
print "Hey - who do you think you're kidding?\n";
}
كما ترى فإن الجزء الأكثر أهمية في البرنامج هو التعبير المنتظم ، و سنناقشه هنا :
^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/)
الجزء الأول من هذا التعبير :
^([a-zA-Z0-9_-])
يطابق الجزء الخاص بإسم المستخدم في عنوان البريد ، و هو إما أن يكون حرف من a إلى z أو من A إلى Z أو رقم من صفر إلى 9 أو خليط منهم .
يتبع هذا الجزء علامة @ التي يتبعها اسم المجال في عنوان البريد ، و هو مرة أخرى يمكن أن يتضمن حروف أو أرقام أو خليط منهما .
ثم في الجزء الأخير نستخدم النقطة (dot) مسبوقة برمز الهروب لأنها كما عرفنا من الرموز المتغيرة . ثم يتبعها الجزء الأخير من العنوان .
كما هو جلي فإن هذا المثال لمجرد التوضيح ، فإذا أردت أن تستخدمه على الويب يجب عليك أن تحسنه و تعدله قليلاً ، على سبيل المثال هذا البرنامج لا يقبل عناوين البريد الإلكتروني التي تكون بهذه الصيغة firstname.lastname@somedomain.com و هي شائعة على الويب .
و أخيراً فإنه من الواضح أن هناك الكثير مما يمكن أن تفعله بالتعبيرات المنتظمة بخلاف التأكد من صيغة البريد الإلكتروني ، فيمكنك أن تستخدمها مثلاً في التأكد من صحة أرقام التليفونات أو عناوين URL أو أشكال العملات و الكثير من الأشياء . كل ما تحتاجه هو الإبداع و الصبر و بعض شرائح البيتزا (بالنسبة لي سوف تكون بعض شطائر الفول) بالإضافة إلى ..... طبيب معالج يهتم لأمرك .
تأليف : فيكرام فاسواني و هاريش كاماث في melonfire
ترجمة : -=Single Plural=-