متى تلجأ إلى التعابير النمطية؟
التعابير النمطية (regex) هي لغة صغيرة تستخدمها لوصف أنماط النصوص. قوية جداً، ومن السهل الإفراط في استخدامها.
قبل أن تستورد re، اسأل نفسك: هل تكفي دوال السلاسل النصية العادية؟ فاستخدام .split() و.replace() و.startswith() أو "target" in text أسرع وأوضح للقراءة وأقل عُرضة للأخطاء من أي تعبير نمطي مكافئ. احتفظ بـ regex بايثون للحالات التي يكون فيها النمط الذي تبحث عنه ذا بنية حقيقية، لكنه أكثر مرونة من أن تتعامل معه بعمليات نصية ثابتة: عناوين البريد الإلكتروني، أرقام الهواتف، أسطر سجلات (logs) ذات شكل معروف، مقاطع HTML أو Markdown، وكل عمليات البحث من نوع "جِد أي شيء يشبه هذا".
المفردات الأساسية في regex
النمط (pattern) في التعابير النمطية مجرد سلسلة نصية تصف ما تبحث عنه. إليك أهم اللبنات:
.— أي محرف مفرد\d— أي رقم (من 0 إلى 9)\w— أي محرف "كلمة" (حرف أو رقم أو شرطة سفلية)\s— أي محرف فراغ^— بداية النص$— نهاية النص[abc]— أي واحد من a أو b أو c[^abc]— أي محرف ما عدا a و b و ca|b— إما a أو b*— صفر أو أكثر من العنصر السابق+— واحد أو أكثر?— صفر أو واحد{3}— ثلاث مرات بالضبط{2,5}— من 2 إلى 5 مرات( ... )— مجموعة (تلتقط ما يطابقها بداخلها)
هذا القدر يكفيك لمعظم الاستخدامات الواقعية.
أهم دوال وحدة re في بايثون
الدالة re.search(pattern, text) تبحث عن أول تطابق في أي موضع داخل النص، وتُعيد كائن تطابق (match object) أو None:
استخدم دائماً السلاسل الخام (r"...") عند كتابة أنماط regex في بايثون. خلاف ذلك ستحاول بايثون تفسير الشرطات المائلة العكسية كرموز هروب داخل النص، فينتهي بك الأمر بنمط مختلف تماماً عمّا كتبته.
الدالة re.match(pattern, text) تشبه search، لكنها تبحث عن تطابق في بداية النص فقط:
في معظم الحالات ستحتاج إلى search.
الدالة re.findall(pattern, text) تُرجع جميع التطابقات غير المتداخلة على شكل قائمة:
انتبه إلى أنّ findall يُرجِع سلاسل نصية، لا كائنات match. إذا كان النمط يحتوي على مجموعة واحدة، ستحصل على محتوى تلك المجموعة مباشرة. أمّا إذا كان فيه أكثر من مجموعة، فستحصل على قائمة من الـ tuples.
المجموعات الملتقِطة (Capturing Groups) في regex بايثون
الأقواس داخل النمط تلتقط كل ما طابقها في النص:
المجموعات المُسمّاة (named groups) عادةً ما تكون أوضح:
عندما يحتوي التعبير النمطي على أكثر من مجموعة، فإن تسميتها يجعل كود استخراج البيانات أوضح وأسهل في القراءة.
استبدال النصوص باستخدام re.sub
الدالة re.sub(pattern, replacement, text) تقوم باستبدال كل التطابقات في النص:
هذا السطر يحذف كل شيء باستثناء الأرقام. ويمكن للنص البديل أن يشير إلى المجموعات الملتقطة عبر \1 و\2 وهكذا، أو يمكنك استخدام \g<1> مع الـ raw strings لأنها أوضح:
كذلك يمكنك تمرير دالة مكان النص البديل، وهذا مفيد في التحويلات التي لا تقتصر على مجرد استبدال مباشر:
تجميع الأنماط لإعادة استخدامها (Compiling Patterns)
لو بتستخدم نفس النمط أكثر من مرة — خاصة داخل حلقة تكرارية — الأفضل تجمّعه مرة واحدة فقط:
الأنماط المُجمَّعة توفّر نفس الدوال — search وmatch وfindall وsub — لكن دون الحاجة لتمرير النمط كوسيط أول. هذا أكثر كفاءة بقليل، وغالبًا أوضح في القراءة.
الأعلام (Flags)
هذه أشهر المُعدِّلات، وتُمرَّر عبر الوسيط flags= أو تُدمَج باستخدام عامل OR:
re.IGNORECASE(re.I) — المطابقة دون التفريق بين الأحرف الكبيرة والصغيرة.re.MULTILINE(re.M) — يجعل^و$يطابقان بداية ونهاية كل سطر، لا حدود النص فقط.re.DOTALL(re.S) — يجعل.يطابق محارف السطر الجديد أيضًا (افتراضيًا لا يطابقها).re.VERBOSE(re.X) — يسمح بإضافة مسافات وتعليقات بـ#داخل النمط، لتحسين القراءة.
re.VERBOSE مفيد جدًا عند التعامل مع التعابير النمطية المعقّدة:
التعابير النمطية متعددة الأسطر والمُعلَّق عليها أسهل بكثير في الصيانة من تلك المكتوبة في سطر واحد غامض.
الجشع مقابل الكسول (Greedy vs Lazy)
المُكمِّمات (*، +، ?، {n,}) في regex بايثون تكون جشعة افتراضيًا، أي أنها تحاول التقاط أكبر قدر ممكن من النص. ولجعلها كسولة، أضف ? بعدها:
النسخة الجشعة (greedy) تلتقط النص كاملاً من <b> حتى </i> — وهذا غالباً ليس ما تريده. أما النسخة الكسولة .+? فتتوقف عند أول > تقابله.
(ملاحظة جانبية: لا تستخدم regex لتحليل HTML في مشاريع حقيقية. استعن بوحدة html.parser أو مكتبة BeautifulSoup. المثال أعلاه لمجرد توضيح فكرة الجشع.)
مثال واقعي
لنحلل أسطر ملف log بصيغة بسيطة:
قوة كبيرة في أسطر قليلة جدًا.
عادات مفيدة
- استخدم دائمًا النصوص الخام (raw strings) عند كتابة الأنماط.
- ابدأ بأبسط نمط يؤدي المطلوب، ثم أحكِمه لاحقًا.
- استخدم المجموعات المُسمّاة (named groups) بمجرد أن يتجاوز عدد المجموعات مجموعة واحدة.
- اعمل compile للأنماط التي ستعيد استخدامها.
- التعابير النمطية في بايثون أبطأ من دوال النصوص العادية. إذا كانت
.split()تكفي، فاستعمل.split().
التالي: الأخطاء وتصحيحها
بهذا نختم جولتنا مع البيانات الحقيقية: الملفات، JSON، CSV، HTTP، التواريخ، ثم regex بايثون. لكن أي برنامج حقيقي لا بدّ أن يصطدم بخطأ عاجلًا أم آجلًا، وقراءة رسائل الـ traceback في بايثون بفهم هي أهم مهارة تصحيح أخطاء ستكتسبها على الإطلاق. الفصل الأخير يغطي الاستثناءات وأكثر الأخطاء التي ستقابلك في طريقك.
الأسئلة الشائعة
ما هي التعابير النمطية (Regex) في بايثون؟
التعبير النمطي (Regex) هو لغة صغيرة مخصصة لوصف الأنماط داخل النصوص. وحدة re في بايثون تتيح لك البحث عن هذه الأنماط، استخراج أجزاء منها، واستبدال ما يطابقها. طبعاً هي مبالغة لو كانت مهمتك بسيطة — في هذه الحالة ابدأ بدوال النصوص مثل .split() أو .replace() — لكنها لا تُضاهى حين تتعامل مع أنماط منظمة ومعقدة.
ما الفرق بين re.match و re.search؟
re.match يطابق فقط من بداية النص، بينما re.search يفحص النص بالكامل ويُعيد أول تطابق يجده في أي موضع. إذا كنت متردداً فاستخدم search — فهو أقرب لما يتوقعه الإنسان فعلياً.
هل يجب دائماً استخدام raw string مع أنماط Regex؟
نعم، يُفضّل ذلك. أنماط Regex تحتوي عادةً على شرطات مائلة عكسية مثل \d و \s و \b، وبايثون تتعامل معها كرموز هروب داخل النصوص العادية. وضع الحرف r قبل النص — مثل r'\d+' — يُخبر بايثون أن تأخذه حرفياً كما هو، فيبقى النمط واضحاً ومقروءاً.