عند البحث عن شيء ما باستخدام التعبيرات المنتظمة، بالتأكيد تريد تحديد كمية وعدد المطابقات، بحيث تستطيع البحث عن:
لاشيء أو أكثر واحدة أو أكثر لاشيء أو واحدة فقط تحديد عدد n من المطابقات تحديد أقصى عدد وأقل عدد من المطابقاتلكي نفهم محددات الكميات علينا أولا اختبار نمط ما بدون محددات حتى نعرف الفرق. وليكن أننا نريد مطابقة الحرف a في الكلمة bbbaaaa، فنقوم بكتابة النمط التالي
<?php preg_match("/a/",'bbbaaaa', $m); var_dump($m); ?>نجد أن النتيجة هي
array(1) { [0]=> string(1) "a" }وهذا يعني أن النمط بهذا الشكل يقوم بمطابقة أول ظهور للحرف a إنطلاقاً من النقطة التي يقف عندها المؤشر. ولكن ماذا عن باقي الأحرف a في الكلمة؟
لكي نحصل على جميع المطابقات، هذا يحتاج منا تكرار تطبيق النمط بعد كل عملية مطابقة، لأنه من وجهة نظر المحرك فهو عند الحصول على المطابقة الأولى قد انتهى من عملية المطابقة (لأنه كما عرفنا سابقاً محرك التعبير المنتظم متحمس دائما لاسترجاع النتائج) ونحتاج أن نخبره بأن لايتوقف بعد أول مطابقة وأن يستكمل في باقي النص، لذلك يمكننا يمكننا تغيير إعدادات المحرك بحيث نقوم بتفعيل الإعداد global (لكن سنتخطي ذلك حتى تعرف عن الإعدادت) أو الإعتماد على الدالة preg_match_all والتي ستقوم بتكرار النمط على باقي النص بعد أول مطابقة. وبالتالي عند تطبيق الدالة على نفس الكلمة هكذا:
<?php preg_match_all("/a/",'bbbaaaa', $m); echo '<pre>'; print_r($m); echo '</pre>'; ?>وتكون النتيجة
Array ( [0] => Array ( [0] => a [1] => a [2] => a [3] => a ) )الآن إذا قمت بعمل استبدال لنتائج المطابقة باستخدام preg_replace وليكن بـ Ahmed سيتم استبدال كل a بـ Ahmed، ولكن ربما يكون المطلوب هو استبدال أي سلسلة من a وليس كل حرف a منفرداً، هنا نحتاج لأن نخبر المحرك بأن لاينتقل في النمط لرمز جديد قبل أن ينتهي من جلب جميع المطابقات للرمز الذي يسبقه، أو بصيغة أخرى نريد أن نخبره بأن يجلب أكبر قدر من المطابقات مع الرمز الحالي في النمط قبل أن ينتقل للرمز الذي بعده.
مثلا النص “bbbaaaacaa” نحتاج للحصول على مطابقة لكل من aaaa و aa وليس كل a منفصلة، هنا يظهر دور الـ Quantifiers، وهم:
علامة * علامة + علامة ? الأقواس المجعدة {} علامة *تسمى * في علوم الحاسب بـ Kleene star، وتقوم العلامة * بمطابقة صفر أو أكثر من الحرف/الرمز الذي نبحث عنه. وأريذ أن أشرح ذلك بأكثر من طريقة حتى تفهم:
بمعنى أنه إذا وجد لاشيء يعتبرها مطابقة وإذا وجد مايبحث عنه فهي بالتأكيد مطابقة. أو النمط لايفشل في حالة عدم وجود ما يبحث عنه، فإن لم يجد شيء يسترجع لاشيء، وإن وجد ما يبحث عنه يسترجع أكبر قدر من الإمكان منه قبل أن ينتقل للرمز الذي يليه في النمط.لاحظ الكود التالي:
<?php preg_match_all("/a*/",'bbbaaaacaa', $m); echo '<pre>'; var_dump($m); echo '</pre>'; ?>سيقوم المحرك بعمل الآتي:
في الوقت الحالي المؤشر يكون في بداية الكلمة، قبل حرف b الأول يقوم المحرك بمحاولة المطابقة فيجد أنه لاشيء، وهذا ما طلبناه من المحرك بمطابقة اللاشيء عن طريق * (أي لاتفشل إذا لم تجد شيئاً)، فيعتبرها مطابقة ويقوم باسترجاع قيمة فارغة، ينتقل المحرك لما بعد الحرف b الأول لفحص مطابقة الحرف التالي وهو b أيضاً والذي لايطابق الحرف a فيسترجع قيمة فارغة لأن اللاشيء يعتبر مطابقة. ينتقل المحرك مرة أخرى لمنطقة اللاشيء قبل الحرف b الثالث وعندها أيضاً لايجد ما يطابق الحرف a وتحدث مطابقة مع اللاشيء ويقوم باسترجاع قيمة فارغة ينتقل المحرك لما قبل الحرف a الأول (بين a ,b) وهنا يجد أن التالي هو حرف a فيعرف أنه سيبدأ الأن بتخزين المتطابقات ابتداءً من أول a، وبما أننا استخدمنا النجمة * فسيقوم بمحاولة جلب أكبر قدر من المطابقات في العملية الواحدة، فيقوم بتخزين جميع أحرف a بين b,c كنتيجة لعملية مطابقة واحدة. بعد أن ينتهي من المطابقة يكون موقع المحرك قبل حرف c وعندما يفحص التالي سيجد أنه لاتوجد مطابقة إلا مع اللاشيء ويسجل عملية مطابقة جديدة باسترجاع قيمة فارغة. ثم ينتقل إلى ما بعد c فيجد أن هناك مطابقة ، وهي الحرفان a بعد c ثم ينتقل ما بعد a الأخيرة فلا يجد شيء وتحدث مطابقة مع اللاشيء ويسترجع قيمة فارغة أيضاً.عند اختبار الكود السابق تكون النتيجة:
array(1) { [0]=> array(7) { [0]=> string(0) "" [1]=> string(0) "" [2]=> string(0) "" [3]=> string(4) "aaaa" [4]=> string(0) "" [5]=> string(2) "aa" [6]=> string(0) "" } }ولتوضيح الأمور أكثر، سنقوم بتطبيق ما قلناه على النص bbbaaaacaa ، بحيث سنتخيل كل عملية مطابقة تم إحلالها بـ x:
قبل b مطابقة مع اللاشيء، فسنضع x قبل b ليصبح xbbbaaaacaa قبل b الثانية مطابقة مع اللاشيء، فسنضع x قبلها ليصبح xbxbbaaaacaa قبل b الثالثة مطابقة مع اللاشيء، فسنضع x قبلها ليصبح xbxbxbaaaacaa بعد b الثالثة مطابقة مع كل أحرف a بين b,c، فسنضع x مكان أحرف a ليصبح xbxbxbxcaa قبل c مطابقة مع اللاشيء، فسنضع x قبلها ليصبح xbxbxbxxcaa بعد c مطابقة مع حرفي a بعدها ، فسنضع x مكان الحرفان ليصبح xbxbxbxxcx عندما يصل لنهاية الجملة لايجد شيء فتحدث مطابقة مع اللاشيء ، فسنضع x مكان اللاشيء ليصبح xbxbxbxxcxxالآن قم باختبار عملية الإحلال باستخدام preg_replace
<?php $string = 'bbbaaaacaa'; echo preg_replace("/a*/",'X' ,$string); ?>وتكون النتيجة XbXbXbXXcXX
بشكل غير تقني للتوضيح أكثر، لاحظ الصورة التالية، والمطلوب أن يقوم الشخص بالصورة البحث عن كل ما هو كورة أو لاشيء ثم يضع علامة صح على مكان المطابقة
علامة +تقوم العلامة + في التعبير المنتظم بالمطابقة مرة واحدة على الأقل من الرمز/الحرف الذي يسبقها ولا تأخذ في الإعتبار اللاشيء. أي أنها تعطي نفس نتيجة * ولكن بدون مطابقة اللاشيء. وبالطبع النمط يفشل إن لم يجد على الأقل مطابقة واحدة في النص.
<?php preg_match_all("/a+/",'bbbaaaacaa',$m); echo '<pre>'; var_dump($m); echo '</pre>'; ?>والنتيجة هي:
array(1) { [0]=> array(2) { [0]=> string(4) "aaaa" [1]=> string(2) "aa" } } علامة ?تقوم علامة ? بمطابقة اللاشيء أو واحدة فقط
<?php preg_match_all("/a?/",'bbbaaaacaa',$m); echo '<pre>'; var_dump($m); echo '</pre>'; ?>وتكون النتيجة:
array(1) { [0]=> array(11) { [0]=> string(0) "" [1]=> string(0) "" [2]=> string(0) "" [3]=> string(1) "a" [4]=> string(1) "a" [5]=> string(1) "a" [6]=> string(1) "a" [7]=> string(0) "" [8]=> string(1) "a" [9]=> string(1) "a" [10]=> string(0) "" } }ويمكن أن تشبهها بـ * ولكن * تقوم بمطابقة ما هو صفر (لاشيء) أو أكثر وبالتالي تستمر في البحث في العملية الواحدة للحصول على أكبر قدر من المطابقات، لكن ? لا تستمر في البحث وتسترجع مطابقة في كل مرة تجد فيها تشابه أو لاشيء.
للتوضيح، فإنه في النص bbbaaaacaa عند استخدام * ، يقوم المحرك البحث عن a وعند أول مطابقة لايتوقف المحرك عن البحث، بل يحاول المطابقة قدر الإمكان، وبالتالي تكون أول نتيجة للمطابقة هي aaaa ثم يبدأ عملية جديدة حتى يسترجع aa كنتيجة لعملية المطابقة الثانية. أما ? عندما تجد أول تطابق لا تحاول البحث أكثر وتقوم باسترجاع نتيجة المطابقة ثم تنتقل لعملية مطابقة أخرى. قم بمقارنة النتيجة في حالة كل من * , ?
البحث الإختياريإذا امعنت النظر في طريقة عمل العلامة ? ستجد أنه يمكن استخدامها لأن تجعل بعض العناصر في البحث اختيارية. فمثلا colou?r تطابق كلا من colour و color.
هناك أيضا مايسمى بالمجموعات الإلتقاطية Regex capturing group (نتحدث عنها في موضوع خاص) يمكن أيضاً أن نجعلها اختيارية هكذا، فمثلاً Nov(ember)? تطابق Nov أو November.
الأقواس المجعدة {}بالتأكيد ستحتاج لعمل حدود للمطابقة، فلا تضطر للتأثير على كل شيء وإنما عدد محدد، وهنا يأتي دور الأقواس المعدة {} ، حيث تستطيع عمل الآتي:
مطابقة عدد n فقط: {n} مطابقة على الأقل n مرة: {n,} مطابقة على الأقل n وعلى الأكثر m مرة: {n,m} مطابقة عدد n فقط: {n}في الكود التالي سنقوم بإجبار محرك التعبيرات المنتظمة بأن يقوم بمطابقة حرفان a متتاليان هكذا
<?php preg_match_all("/a{2}/",'bbbaaaacaa',$m); echo '<pre>'; var_dump($m); echo '</pre>'; ?>فتكون النتيجة:
array(1) { [0]=> array(3) { [0]=> string(2) "aa" [1]=> string(2) "aa" [2]=> string(2) "aa" } }لاحظ أنه مجرد ما وجد مطابقة للحرف a قام بالبحث مرة أخرى حتى يجد مطابقة ثانية، وما أن حصل عليها، قام باسترجاع نتيجة المطابقة ثم بدأ عملية جديدة. ماذا إذا غيرنا الرقم إلى 3؟. قم بتغيير الرقم ولاحظ النتيجة
array(1) { [0]=> array(1) { [0]=> string(3) "aaa" } }عندما انتهى المحرك من مطابقة النمط للحصول على 3 مطابقات للحرف a، قام باسترجاع النتيجة، وعندما حاول تكرار المطابقة مرة أخرى لم يجد 3 أحرف أخرى متتالية للحرف a. ماذا تتوقع إذا قمنا بوضع 1؟
مطابقة على الأقل n مرة: {,n}سنقوم بالتعديل على الكود لجلب على الأقل 4 مطابقات
<?php preg_match_all("/a{4,}/",'bbbaaaacaa',$m); echo '<pre>'; var_dump($m); echo '</pre>'; ?>تكون النتيجة:
array(1) { [0]=> array(1) { [0]=> string(4) "aaaa" } }لاحظ عندما وجد 4 مطابقات متتالية للحرف قام باسترجاع النتيجة للأربعة أحرف الأولى أما الحرفان بعد c ليست مطابقة لأنها أقل من 4. قم بتغيير الرقم إلى 5 ولاحظ النتيجة.
مطابقة على الأقل n وعلى الأكثر m مرة: {n,m}بالطبع كما هو متوقع، سنقوم بتعديل البحث بحيث نسمح للمحرك بجلب المطابقات إن كانت على الأقل n أو على الأكثر m،
<?php preg_match_all("/a{2,4}/",'bbbaaaacaa',$m); echo '<pre>'; var_dump($m); echo '</pre>'; ?>وتكون النتيجة:
array(1) { [0]=> array(2) { [0]=> string(4) "aaaa" [1]=> string(2) "aa" } }ماذا إذا قمنا بتغيير عدد أحرف a نفسها قبل c لتكون 1 فقط هكذا bbbacaa؟ ماذا تتوقع أن تكون النتيجة؟
رغم أن أقصى عدد هو 4، لكن a قبل c لا تعتبر مطابقة لأنها أقل من الحد الأدنى 2، وتكون النتيجة فقط هي حرفي a بعد c:
array(1) { [0]=> array(1) { [0]=> string(2) "aa" } }عندما كنا تلاميذ، كان دائما مايكون هناك سؤال للطلبة المتفوقين، وفي الحقيقة كانت أسئلة تحتاج للذكاء، لذلك سأقوم بإقتباس الفكرة وأضع سؤال يحتاج القليل من الذكاء.
[highlight background=”” color=””]يمكن أيضاً استخدام الأقواس المجعدة في عمل البحث الإختياري بحيث نجعل حدود البحث هي صفر أو واحد فقط مثل ? هكذا {0,1} [/highlight]
مالذي يحدث عند تطبيق {,0}؟
قم بالتجربة وأخبرني في التعليقات ما هي ملاحظتك على النتيجة!!



