قائمة البرمجة > لغة Perl

التعبيرات المنتظمة Regular Expression

الكاتب:


المصدر: http://www.angelfire.com/rnb/forarabs

ما هي التعبيرات المنتظمة ؟


التعبيرات المنتظمة 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 أو أشكال العملات و الكثير من الأشياء . كل ما تحتاجه هو الإبداع و الصبر و بعض شرائح البيتزا (بالنسبة لي سوف تكون بعض شطائر الفول) بالإضافة إلى ..... طبيب معالج يهتم لأمرك .