2.36. المجموعات والمراسي

يمكن للنمط أن يفعل أكثر من قول "هذه السلسلة مطابقة" -- إذ يمكنه تفكيك الأجزاء المطابقة وتسليم كل واحد منها إلى التطبيق بالاسم. الأقواس حول جزء من النمط تجعله مجموعة التقاط (capturing group)؛ ثم يكشف كائن المطابقة عن كل مجموعة كسلسلة فرعية منفصلة.

2.36.1. مجموعات الالتقاط

غلّف أي جزء من النمط داخل (...) لالتقاط ما طابقه:

>>> import re
>>> m = re.search(r'temp (\d+) at (\d+)s', 'temp 42 at 137s ok')
>>> m.group(0)
'temp 42 at 137s'
>>> m.group(1)
'42'
>>> m.group(2)
'137'
  • المجموعة 0 هي دائماً المطابقة الكاملة.

  • المجموعات 1 و2 و... هي السلاسل الفرعية الملتقطة، مرقّمة من اليسار إلى اليمين حسب القوس الافتتاحي الخاص بها.

  • استدعاء match.group() بفهرس يتجاوز آخر مجموعة يثير IndexError.

من الأنماط الشائعة "طابق بنية معروفة، والتقط الأجزاء المتغيرة كأعداد صحيحة":

def parse_temp(line):
    m = re.search(r'temp (\d+) at (\d+)s', line)
    if not m:
        return None
    return int(m.group(1)), int(m.group(2))

2.36.2. المجموعات غير الملتقِطة

الأقواس أيضاً تُجمّع تعبيراً فرعياً بحيث يمكن لمحدّد الكمية أن ينطبق على المجموعة بأكملها. هذا هو الغرض الوحيد من التجميع في r'(ab)+' -- "واحد أو أكثر من ab". أما ظهور ab كالمجموعة 1 فهو أثر جانبي.

للتجميع دون التقاط، استخدم (?:...)

>>> re.search(r'(?:ab)+', 'xababy').group(0)
'abab'

تُبقي المجموعات غير الملتقِطة أرقام المجموعات مرتّبة عندما يستخدم النمط التجميع من أجل البنية لكنه لا يهتم باستخراج كل جزء على حدة.

2.36.3. المراسي

لا تطابق المراسي حرفاً -- بل تطابق موضعاً.

  • ^ -- بداية السلسلة.

  • $ -- نهاية السلسلة.

المراسي هي ما يجعل re.match() وre.search() يتصرفان بشكل مختلف. إن re.match(p, s) يكافئ re.search('^' + p, s): فهو يفرض على النمط أن يبدأ عند الموضع 0. ثم إن إضافة $ إلى نهاية النمط تجعل النمط يطابق السلسلة بأكملها ولا شيء غير ذلك:

>>> re.search(r'^\d+$', '12345')
<match num=1>
>>> re.search(r'^\d+$', '12345 ok') is None
True

إن ^ و$ في re الخاصة بـ MicroPython تعنيان دائماً بداية ونهاية السلسلة الكاملة المُمرَّرة إلى re.search(). لا توجد راية re.MULTILINE تجعلهما يطابقان عند كل سطر جديد مضمّن، كما أن $ لا يطابق الموضع الذي يسبق \n ختامياً أيضاً -- بل يجب أن يكون النهاية المطلقة للمدخل. للحصول على سلوك يعمل سطراً بسطر، قسّم المدخل على الأسطر الجديدة أولاً ثم شغّل النمط على كل سطر.

2.36.4. مجموعات الأحرف

تعرّف الأقواس المربعة مجموعة صريحة من الأحرف. تستهلك المطابقة حرفاً واحداً بالضبط من المجموعة.

  • [abc] -- واحد من a أو b أو c.

  • [a-z] -- حرف واحد في النطاق a-z (شامل).

  • [a-zA-Z0-9] -- حروف أو أرقام. ثلاثة نطاقات مدمجة.

  • [^abc] -- ليس واحداً من a أو b أو c. إن ^ لا يفيد النفي إلا حين يكون الحرف الأول داخل الأقواس.

أمثلة:

>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest').group(0)
'1A2B3C'
>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest') is None
True

يُعيد الاستدعاء الأول None عملياً لأن النص الحرفي بحروف صغيرة. لا تملك re الخاصة بـ MicroPython راية re.IGNORECASE -- للمطابقة دون تمييز حالة الأحرف، اكتب كلتا الحالتين داخل المجموعة:

>>> re.search(r'[A-Fa-f0-9]{6}', 'colour #1a2b3c rest').group(0)
'1a2b3c'

يمكن استخدام اختصارات الفئة (\d و\s و\w وأشكالها المنفية) داخل [...] أيضاً: فإن [\w-] تعني "أحرف الكلمات أو شَرطة حرفية."

2.36.5. محدّدات الكمية الجشعة مقابل الكسولة

محدّدات الكمية * و+ و? و{m,n} هي جشعة افتراضياً -- إذ تطابق أكبر عدد من الأحرف مما تسمح به بقية النمط. وغالباً ما يكون ذلك هو المطلوب بالضبط؛ وأحياناً لا يكون كذلك:

>>> re.search(r'<(.+)>', 'a <b> <c> d').group(1)
'b> <c'

التقط .+ الجشع كل المسافة حتى آخر >. وإلحاق ? يجعل محدّد الكمية كسولاً -- إذ يطابق أقل قدر ممكن:

>>> re.search(r'<(.+?)>', 'a <b> <c> d').group(1)
'b'

يتوقف الشكل الكسول عند أول >. تظهر محدّدات الكمية الكسولة باستمرار عند استخراج الفواصل المتوازنة من سلسلة.

2.36.6. المراجع العكسية في الاستبدال

يمكن لـ re.sub() أن تشير عكسياً إلى المجموعات الملتقطة في سلسلة الاستبدال عبر \1 و\2 و... يعيد الاستبدال كتابة كل مطابقة باستخدام الأجزاء الملتقطة:

>>> re.sub(r'(\d+)\.(\d+)', r'\2.\1', 'swap 12.34 and 5.6')
'swap 34.12 and 6.5'

تلتقط كل مطابقة رقمين، ويبدّل الاستبدال موضعيهما. إن \g<1> هي صياغة بديلة للشيء نفسه -- وهي مفيدة عندما يكون الحرف التالي في الاستبدال رقماً (r'\g<1>0' لإلحاق صفر حرفي بالمجموعة 1 بدلاً من قراءتها "المجموعة 10").

2.36.7. ما هو غير متاح

تذكير بما لا تدعمه re الخاصة بـ MicroPython، تحسباً لوصول نمط من CPython إلى هنا ومفاجأتك:

  • الاستطلاع الأمامي (?=...) والاستطلاع الخلفي (?<=...) -- غير منفّذَين.

  • المجموعات المسماة (?P<name>...) والمراجع العكسية المسماة (?P=name) -- غير منفّذة.

  • ثوابت الرايات مثل re.IGNORECASE وre.MULTILINE وre.DOTALL -- غير مدعومة. ابنِ المجموعة غير المميزة لحالة الأحرف أو قسّم المدخل مسبقاً بنفسك.

  • إن الدوال match.groups() وmatch.span() وmatch.start() وmatch.end() محصورة عند مستوى ROM لا تفعّله أي لوحة OpenMV مشحونة. لن يعمل الكود الذي يعتمد عليها على الكاميرا.

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