التعبيرات العادية

التعبير العادي (regex) هو لغة خاصة لوصف أنماط النص. سطر واحد من regex يمكنه فعل ما يتطلبه عشرات الأسطر من الكود اليدوي لمطابقة واستخراج النص. إنه موجود في كل مكان في تنظيف البيانات، تحليل السجلات، والتحقق من صحة النماذج.


1. ما هو التعبير العادي

التعبير العادي يستخدم رموزاً خاصة لوصف "أي نوع من السلاسل النصية أبحث عنه."

PYTHON
import re

# أبسط تعبير عادي: طابق سلسلة مباشرة
pattern = r"hello"
text = "hello world"

result = re.search(pattern, text)
if result:
    print("Found!")                 # Found!
    print(result.group())           # hello
    print(result.start())           # 0 (موضع البداية)
    print(result.end())             # 5 (موضع النهاية)
⚠️ دائماً اسبق سلاسل التعبير العادي بـ r (سلسلة أولية). r"\n" هي شرطة مائلة + حرف n، وليس سطراً جديداً. التعبيرات العادية تستخدم الشرطات المائلة العكسية بكثرة؛ بدون r، ستحتاج لكتابة \\n، وهو أمر مؤلم.


2. الطرق الشائعة

PYTHON
import re

text = "My email is alice@example.com, also bob@test.com"

# search() — ابحث عن أول تطابق
result = re.search(r"\w+@\w+\.\w+", text)
print(result.group())               # alice@example.com

# findall() — ابحث عن جميع التطابقات
emails = re.findall(r"\w+@\w+\.\w+", text)
print(emails)                       # ['alice@example.com', 'bob@test.com']

# match() — طابق من البداية
print(re.match(r"My", text))        # تطابق (في البداية)
print(re.match(r"email", text))     # None (ليس في البداية)

# sub() — استبدال
masked = re.sub(r"\w+@", "***@", text)
print(masked)                       # My email is *@example.com, also *@test.com

3. مرجع سريع للرموز الوصفية

الرمز المعنى مثال يطابق
. أي حرف واحد (باستثناء السطر الجديد) h.t hat, hot, hit
\d رقم \d{3} 123, 456
\w حرف/رقم/شرطة سفلية \w+ hello, abc123
\s مسافة بيضاء \s مسافة، تبويب، سطر جديد
* 0 أو أكثر مما قبله ab*c ac, abc, abbc
+ 1 أو أكثر مما قبله ab+c abc, abbc (ليس ac)
? 0 أو 1 مما قبله colou?r color, colour
{n} بالضبط n مرة \d{4} 2026, 1990
{n,m} من n إلى m مرة \d{2,4} 23, 456, 2026
^ بداية السلسلة ^Hello Hello...
$ نهاية السلسلة end$ ...end
[] مجموعة أحرف [aeiou] أي حرف علة
` ` أو `cat

مثال: التحقق من صحة رقم الهاتف (الصعوبة ⭐⭐)

PYTHON
import re

def is_valid_phone(phone):
    """تحقق من رقم هاتف محمول صيني (11 رقماً، يبدأ بـ 1)"""
    pattern = r"^1[3-9]\d{9}$"
    return bool(re.match(pattern, phone))

phones = ["13800138000", "12345678901", "010-12345678", "1380013800a"]
for p in phones:
    print(f"{p}: {'✅' if is_valid_phone(p) else '❌'}")

# استخرج جميع أرقام الهواتف من النص
text = "Contact: 13800138000, backup: 13912345678, landline: 010-12345678"
phones = re.findall(r"1[3-9]\d{9}", text)
print(f"Found phones: {phones}")
▶ جرّب الكود

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

استخدم الأقواس () لتقسيم المحتوى المطابق إلى أجزاء متعددة:

PYTHON
import re

# استخرج اسم المستخدم والنطاق من البريد الإلكتروني
email = "alice@example.com"
pattern = r"(\w+)@(\w+\.\w+)"
result = re.search(pattern, email)

if result:
    print(f"Full match: {result.group(0)}")    # alice@example.com
    print(f"Username: {result.group(1)}")      # alice
    print(f"Domain: {result.group(2)}")        # example.com

# استخرج معلومات من سجل
log = "2026-06-23 15:30:45 ERROR Database connection timeout"
pattern = r"(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (\w+) (.+)"
result = re.search(pattern, log)
print(f"Date: {result.group(1)}")           # 2026-06-23
print(f"Time: {result.group(2)}")           # 15:30:45
print(f"Level: {result.group(3)}")          # ERROR
print(f"Message: {result.group(4)}")        # Database connection timeout

مثال: محلل URL (الصعوبة ⭐⭐⭐)

PYTHON
import re

def parse_url(url):
    """حلل URL لاستخراج البروتوكول والنطاق والمسار ومعاملات الاستعلام"""
    pattern = r"(https?)://([^/]+)(/[^?]*)?(?:\?([^#]*))?"
    result = re.search(pattern, url)
    if not result:
        return None
    
    return {
        "protocol": result.group(1),
        "domain": result.group(2),
        "path": result.group(3) or "/",
        "query": result.group(4) or ""
    }

urls = [
    "https://www.example.com/path/page.html?id=123&name=test",
    "http://localhost:8080/api/users",
    "https://google.com"
]

for url in urls:
    info = parse_url(url)
    if info:
        print(f"\nURL: {url}")
        for key, value in info.items():
            print(f"  {key}: {value}")
▶ جرّب الكود

5. المطابقة الجشعة مقابل الكسولة

افتراضياً، * و + هما جشعان — يطابقان أكبر قدر ممكن. إضافة ? تجعلهما كسولين — يطابقان أقل قدر ممكن:

PYTHON
import re

text = "<h1>Title</h1><p>Paragraph</p>"

# الوضع الجشع — يطابق أكبر قدر ممكن
greedy = re.search(r"<.+>", text)
print(greedy.group())               # <h1>Title</h1><p>Paragraph</p>

# الوضع الكسول — يطابق أقل قدر ممكن (أضف ?)
lazy = re.search(r"<.+?>", text)
print(lazy.group())                 # <h1>
💡 .*? (كسول) مقابل .* (جشع): عند العمل مع محتوى منظم مثل HTML أو JSON، الوضع الكسول أكثر شيوعاً. الوضع الجشع غالباً "يتجاوز" ليطابق محتوى لا ينبغي له. إذا كنت غير متأكد، ابدأ بالكسول.


حالات الاستخدام الشائعة


❓ أسئلة شائعة

س ماذا تعني البادئة r في التعبير العادي؟
ج r تشير إلى سلسلة أولية. في سلسلة عادية، \n هو سطر جديد؛ في سلسلة أولية، \n هو مجرد شرطة مائلة + حرف n. التعبيرات العادية تستخدم الشرطات المائلة العكسية بكثرة (مثل \d, \w)؛ بدون r، ستحتاج لكتابة \\d، وهو أمر مؤلم جداً. س:ما أداء التعبيرات العادية؟ ج:سريعة بما يكفي لمعظم الحالات. لكن الأنماط المكتوبة بشكل سيئ (مثل الكميات المتداخلة (.*)*) يمكن أن تسبب "تراجعاً كارثياً" — يستغرق دقائق لنص بسيط. الحلول: تجنب الكميات المتداخلة، استخدم الوضع الكسول، واستخدم re.compile() للأنماط المستخدمة frequently. س:التعبير العادي المعقد صعب القراءة. أي نصائح؟ ج:① أضف تعليقات — الوضع re.VERBOSE يسمح بمسافات وتعليقات في regex. ② قسمه إلى أنماط بسيطة متعددة. ③ استخدم أدوات عبر الإنترنت مثل regex101.com للتصحيح. Regex هو الكلاسيكي "ممتع في الكتابة، مؤلم في القراءة" — لا تحاول حل كل شيء بـ regex واحد.

📖 ملخص

  • re.search() يجد أول تطابق؛ findall() يجد الكل؛ sub() يستبدل
  • الرموز الوصفية: \d رقم، \w حرف كلمة، \s مسافة بيضاء، . أي حرف
  • الكميات: * أي، + على الأقل 1، ? 0 أو 1، {n} بالضبط n
  • المجموعات () تستخرج أجزاء؛ group(1) يحصل على المجموعة الأولى
  • الجشع يطابق أكبر قدر ممكن؛ أضف ? للكسول (أقل قدر ممكن)
  • دائماً اسبق التعبير العادي بـ r لتجنب جحيم الشرطات المائلة العكسية

📝 تمارين

  1. أساسي (الصعوبة ⭐): اكتب دالة is_valid_email(email) تستخدم regex للتحقق من صلاحية عنوان البريد الإلكتروني (يحتوي على @، غير فارغ على كلا الجانبين، له لاحقة نطاق).

  2. متوسط (الصعوبة ⭐⭐): اكتب دالة extract_numbers(text) تستخرج جميع الأرقام (بما في ذلك الأعداد الصحيحة والعشرية) من سلسلة وتُرجع مجموعها. مثال: "Price is 19.99, shipping 5, coupon -10" → تُرجع 14.99.

  3. تحدي (الصعوبة ⭐⭐⭐): اكتب "محرك قوالب بسيط." بالنظر إلى القالب "Hello, {name}! Your order {order_id} has been shipped, arriving in {days} days." وقاموس {"name": "Alice", "order_id": "2024001", "days": 3}، استخدم regex لاستبدال جميع العناصر النائبة {variable} بالقيم الفعلية. تلميح: استخدم re.sub() مع دالة استدعاء عكسي.

100%