Kodax

Regular Expressions — Python المتقدم

التعابير النمطية (Regular Expressions)

دوال النصوص (find، split، replace) تبحث عن نص ثابت. التعبير النمطي يبحث عن نمط: "رقم هاتف من 11 خانة"، "أي بريد إلكتروني"، "تاريخ بصيغة سنة-شهر-يوم".

الدوال الأساسية في re

| الدالة | ماذا ترجع |

|---|---|

| re.search(p, s) | أول تطابق في أي مكان، أو None |

| re.match(p, s) | تطابق من بداية النص فقط |

| re.fullmatch(p, s) | هل النص كله يطابق النمط (للتحقق من المدخلات) |

| re.findall(p, s) | قائمة بكل التطابقات |

| re.sub(p, new, s) | استبدال كل التطابقات |

| re.split(p, s) | تقسيم حسب النمط |

لماذا r"..."؟

الأنماط مليئة بـ \\. النص الخام r"\d+" يمنع Python من تفسير الشرطة المائلة، فيصل النمط إلى re كما كتبته. اجعلها عادة دائماً.

اللبنات الأساسية

| الرمز | المعنى |

|---|---|

| . | أي حرف عدا السطر الجديد |

| \d | رقم، \w حرف أو رقم أو _، \s مسافة |

| [abc] | أحد هذه الأحرف، [^abc] أي حرف عداها، [0-9] مجال |

| * | صفر مرة أو أكثر، + مرة أو أكثر، ? اختياري |

| {3} | 3 مرات بالضبط، {2,4} من 2 إلى 4 |

| ^ و $ | بداية النص ونهايته |

| (...) | مجموعة تلتقط جزءاً من التطابق |

| a|b | هذا أو ذاك |

ملاحظة: في Python 3 تطابق \d و \w الأرقام والأحرف العربية أيضاً (Unicode). إذا أردت أرقاماً إنجليزية فقط استخدم [0-9].

المجموعات لاستخراج الأجزاء

import re
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", "التاريخ 2026-03-15")
year, month, day = m.groups()
m = re.search(r"(?P<year>\d{4})-(?P<month>\d{2})", "2026-03")
m["year"]   # مجموعة مسماة

الجشع (greedy)

.* تأخذ أكبر تطابق ممكن: re.findall(r"<.*>", "<a><b>") ترجع ['<a><b>']. أضف ? لتصبح غير جشعة: r"<.*?>" ترجع ['<a>', '<b>'].

الأعلام

re.IGNORECASE لتجاهل حالة الأحرف، و re.MULTILINE لجعل ^ و $ لكل سطر. ولنمط يُستخدم كثيراً: pattern = re.compile(r"...") ثم pattern.findall(text).

تحذير

لا تحاول التحقق من كل شيء بتعبير نمطي ضخم (مثل البريد الإلكتروني بكل قواعده الرسمية، أو تحليل HTML). استخدم نمطاً معقولاً، أو مكتبة متخصصة. والتعبير الذي لا تستطيع قراءته بعد أسبوع مشكلة.

تمرين

من نص سجل، استخرج كل عناوين IP، وكل التواريخ بصيغة YYYY-MM-DD، واستبدل أرقام الهواتف (11 رقماً) بـ ***. ثم اكتب دالة تتحقق بـ fullmatch أن اسم المستخدم من 3 إلى 16 حرفاً إنجليزياً أو رقماً أو _.

import re

text = "بريدي هو test@email.com ورقمي 0501234567"

# search - أول تطابق
match = re.search(r'\d+', text)
if match:
    print(match.group())  # 0501234567

# findall - كل التطابقات
numbers = re.findall(r'\d+', text)
print(numbers)  # ['0501234567']

# match - من البداية فقط
if re.match(r'بريدي', text):
    print("يبدأ بـ 'بريدي'")

# استخراج البريد الإلكتروني
email_pattern = r'[\w.-]+@[\w.-]+\.\w+'
emails = re.findall(email_pattern, text)
print(emails)  # ['test@email.com']

# استخراج أرقام الهاتف
phone_pattern = r'05\d{8}'
phones = re.findall(phone_pattern, text)
print(phones)  # ['0501234567']

# الاستبدال
censored = re.sub(r'\d', '*', text)
print(censored)  # بريدي هو test@email.com ورقمي **********

# Groups - مجموعات
date = "2024-12-25"
pattern = r'(\d{4})-(\d{2})-(\d{2})'
match = re.search(pattern, date)
if match:
    year, month, day = match.groups()
    print(f"السنة: {year}, الشهر: {month}, اليوم: {day}")

# Named groups
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
match = re.search(pattern, date)
if match:
    print(match.group('year'))   # 2024
    print(match.group('month'))  # 12

# التحقق من الصحة
def is_valid_email(email):
    pattern = r'^[\w.-]+@[\w.-]+\.\w+$'
    return bool(re.match(pattern, email))

print(is_valid_email("test@example.com"))  # True
print(is_valid_email("invalid-email"))     # False

# تقسيم النص
text = "one, two; three four"
parts = re.split(r'[,;\s]+', text)
print(parts)  # ['one', 'two', 'three', 'four']

# Compile لإعادة الاستخدام
email_regex = re.compile(r'[\w.-]+@[\w.-]+\.\w+')
emails = email_regex.findall("a@b.com and c@d.org")
print(emails)
اختبر فهمك