١. ما هو Regex؟
التعابير النمطية (Regular Expressions) هي لغة صغيرة لوصف أنماط النصوص. فكّر فيها كـ "بحث متطور جداً". بدلاً من البحث عن كلمة محددة، تصف شكل ما تريده.
مثال بسيط: للتحقق من أن إيميلاً صحيح، regex:
^[\w.+-]+@[\w-]+\.[a-z]{2,}$ — بسطر واحد يُغني عن عشرات الشروط.Python — البداية
import re
text = "اتصل بنا على 0612345678 أو 0523456789"
# هل يحتوي النص على رقم هاتف مغربي؟
if re.search(r'\b0[5-7]\d{8}\b', text):
print("✅ وُجد رقم هاتف")
# استخراج كل الأرقام
phones = re.findall(r'0[5-7]\d{8}', text)
print(phones) # ['0612345678', '0523456789']
دائماً استخدم raw string: اكتب
r'\d+' وليس '\d+' لتجنب تفسير الـ backslash.٢. الرموز الأساسية — جدول مرجعي
رموز المطابقة
| الرمز | المعنى | مثال | يطابق |
|---|---|---|---|
| . | أي حرف (عدا سطر جديد) | a.c | abc, a1c, a-c |
| \d | رقم (0-9) | \d\d\d | 123, 456 |
| \D | ليس رقماً | \D+ | abc, هاي |
| \w | حرف أو رقم أو _ | \w+ | hello, test_1 |
| \W | ليس حرفاً أو رقماً | \W | !, @, مسافة |
| \s | مسافة / tab / سطر جديد | \s+ | فراغات |
| \S | ليس مسافة | \S+ | كلمات |
| ^ | بداية النص | ^Hello | Hello world |
| $ | نهاية النص | end$ | the end |
| \b | حدود الكلمة | \bcat\b | cat (ليس catch) |
رموز التكرار
| الرمز | المعنى | مثال | يطابق |
|---|---|---|---|
| * | صفر أو أكثر | ab*c | ac, abc, abbc |
| + | واحد أو أكثر | ab+c | abc, abbc (ليس ac) |
| ? | صفر أو واحد | colou?r | color, colour |
| {n} | بالضبط n مرة | \d{4} | 2025 |
| {n,m} | من n إلى m مرة | \d{2,4} | 12, 123, 1234 |
| [abc] | أي من هذه الأحرف | [aeiou] | أي حرف علة |
| [^abc] | ليس هذه الأحرف | [^0-9] | أي حرف غير رقمي |
| a|b | a أو b | cat|dog | cat أو dog |
٣. دوال مكتبة re
Python — الدوال الأساسية
import re
text = "البريد: ahmed@gmail.com والهاتف: 0612345678"
# ===== re.search() — أول تطابق =====
match = re.search(r'\d{10}', text)
if match:
print(match.group()) # 0612345678
print(match.start()) # موقع البداية
print(match.end()) # موقع النهاية
# ===== re.findall() — كل التطابقات =====
emails = re.findall(r'[\w.+-]+@[\w-]+\.\w+', text)
print(emails) # ['ahmed@gmail.com']
# ===== re.finditer() — iterator من matches =====
long_text = "رقم 123 وآخر 456 وأخير 789"
for m in re.finditer(r'\d+', long_text):
print(f"وجدت '{m.group()}' في موقع {m.start()}-{m.end()}")
# ===== re.sub() — بحث وإستبدال =====
dirty = "السعر: 500 درهم"
clean = re.sub(r'\s{2,}', ' ', dirty) # يزيل المسافات الزائدة
print(clean) # "السعر: 500 درهم"
# إخفاء أرقام البطاقات
card = "رقم بطاقتك: 4532-1234-5678-9012"
hidden = re.sub(r'\d{4}-\d{4}-\d{4}', 'XXXX-XXXX-XXXX', card)
print(hidden) # رقم بطاقتك: XXXX-XXXX-XXXX-9012
# ===== re.split() — تقسيم بنمط =====
data = "خليل,فاطمة;أحمد سارة"
parts = re.split(r'[,;\s]+', data)
print(parts) # ['خليل', 'فاطمة', 'أحمد', 'سارة']
# ===== re.compile() — تجميع النمط للاستخدام المتكرر =====
phone_re = re.compile(r'0[5-7]\d{8}')
print(phone_re.findall("0661234567 و 0522345678"))
🧠 سؤال سريع
ما الفرق بين re.search() و re.findall()؟
✅ صحيح!
search() يقف عند أول تطابق، بينما findall() يمشي في كامل النص ويجمع الكل.❌ خطأ.
search() يُرجع Match object أو None للتطابق الأول فقط، أما findall() فيُرجع قائمة بكل التطابقات.٤. Groups — استخراج أجزاء محددة
الأقواس () في regex تنشئ "مجموعة" يمكن استخراجها بشكل منفصل.
Python — Groups
import re
# استخراج أجزاء التاريخ
text = "تاريخ الميلاد: 15/05/2011"
match = re.search(r'(\d{2})/(\d{2})/(\d{4})', text)
if match:
print(match.group()) # 15/05/2011 (كامل)
print(match.group(1)) # 15 (اليوم)
print(match.group(2)) # 05 (الشهر)
print(match.group(3)) # 2011 (السنة)
# Named groups — أوضح بكثير
pattern = re.compile(r'(?P<day>\d{2})/(?P<month>\d{2})/(?P<year>\d{4})')
m = pattern.search(text)
if m:
print(f"اليوم: {m['day']}, الشهر: {m['month']}, السنة: {m['year']}")
# استخراج كل الإيميلات مع domain منفصل
emails_text = "ahmed@gmail.com و sara@yahoo.fr و khalil@hotmail.com"
pat = re.compile(r'([\w.+-]+)@([\w-]+\.\w+)')
for m in pat.finditer(emails_text):
print(f"مستخدم: {m.group(1):15} | نطاق: {m.group(2)}")
# re.sub مع group reference
dates = "تاريخ: 15/05/2025"
reversed_ = re.sub(r'(\d{2})/(\d{2})/(\d{4})', r'\3-\2-\1', dates)
print(reversed_) # تاريخ: 2025-05-15
٥. Flags — خيارات إضافية
Python — Flags
import re
# re.IGNORECASE — تجاهل حالة الأحرف
text = "Python PYTHON python PyThOn"
found = re.findall(r'python', text, re.IGNORECASE)
print(found) # ['Python', 'PYTHON', 'python', 'PyThOn']
# re.MULTILINE — ^ و $ يعملان مع كل سطر
multi = """Python سهل
Python ممتع
Python مفيد"""
lines = re.findall(r'^Python', multi, re.MULTILINE)
print(len(lines)) # 3
# re.DOTALL — النقطة تطابق السطر الجديد أيضاً
html = "<div>\n مرحباً\n</div>"
inner = re.search(r'<div>(.+?)</div>', html, re.DOTALL)
print(inner.group(1).strip()) # مرحباً
# دمج flags
re.findall(r'python', text, re.IGNORECASE | re.MULTILINE)
🎮 جرّب بنفسك — مختبر Regex
🧪 اكتب نمطاً وشاهد النتيجة فوراً
📝 النص:
🔍 النمط (Regex):
📤 النتائج:
اضغط "تشغيل" لرؤية النتيجة...
🧠 سؤال سريع
ما الذي يطابقه النمط \d{3}-\d{4}؟
✅ ممتاز!
\d{3} = 3 أرقام بالضبط، ثم - حرفية، ثم \d{4} = 4 أرقام.❌ خطأ.
\d{3} يطابق 3 أرقام بالضبط، والشرطة - تطابق نفسها، ثم \d{4} يطابق 4 أرقام.٦. أمثلة حقيقية — Regex في العمل
Python — التحقق من الإيميل وكلمة المرور والهاتف
import re
# ===== ١. التحقق من الإيميل =====
def is_valid_email(email: str) -> bool:
pattern = re.compile(r'^[\w.+-]+@[\w-]+\.[a-z]{2,}$', re.IGNORECASE)
return bool(pattern.match(email))
emails = ["ok@gmail.com", "bad@", "test.user+tag@sub.domain.org"]
for e in emails:
icon = "✅" if is_valid_email(e) else "❌"
print(f"{icon} {e}")
# ===== ٢. التحقق من قوة كلمة المرور =====
def check_password(pwd: str) -> dict:
return {
"الطول ≥ 8" : bool(re.search(r'.{8,}', pwd)),
"حرف كبير" : bool(re.search(r'[A-Z]', pwd)),
"حرف صغير" : bool(re.search(r'[a-z]', pwd)),
"رقم" : bool(re.search(r'\d', pwd)),
"رمز خاص !@#$" : bool(re.search(r'[!@#$%^&*]', pwd)),
}
pwd = "MyPass@2025"
checks = check_password(pwd)
for label, ok in checks.items():
print(f"{'✅' if ok else '❌'} {label}")
print(f"النتيجة: {sum(checks.values())}/5")
# ===== ٣. تنظيف HTML =====
def strip_html(html: str) -> str:
return re.sub(r'<[^>]+>', '', html).strip()
print(strip_html("<h1>عنوان</h1><p>نص مهم</p>"))
# عنوان نص مهم
# ===== ٤. استخراج URLs من نص =====
def extract_urls(text: str) -> list:
return re.findall(r'https?://[\w./%-]+', text)
news = "زر https://python.org و https://docs.python.org/3/"
print(extract_urls(news))
📝 تمارين تطبيقية
تمرين ١ — محلل بيانات السجلات (Log Parser)
لديك سجل خادم (server log). استخرج: التاريخ، رمز الحالة HTTP، وعنوان IP لكل سطر.
Python — الحل
import re
log = """
192.168.1.1 - [15/Mar/2025:10:30:00] "GET /index.html" 200 1234
10.0.0.5 - [15/Mar/2025:10:31:05] "POST /login" 401 56
172.16.0.2 - [15/Mar/2025:10:32:10] "GET /data.json" 200 890
"""
pattern = re.compile(
r'(?P<ip>\d{1,3}(?:\.\d{1,3}){3})'
r'.+\[(?P<date>[^\]]+)\]'
r'.+"(?P<req>[^"]+)"'
r'\s(?P<status>\d{3})'
)
print(f"{'IP':<16} {'Status':<8} {'Date':<25} Request")
print("-" * 70)
for m in pattern.finditer(log):
status_icon = "✅" if m['status'].startswith('2') else "❌"
print(f"{m['ip']:<16} {status_icon}{m['status']:<6} {m['date']:<25} {m['req']}")
تمرين ٢ — محلل جهات الاتصال
اكتب دالة تأخذ نصاً وتستخرج منه جميع الإيميلات وأرقام الهاتف المغربية، وتُرجعها كقاموس.
Python — الحل
import re
from typing import Dict, List
def extract_contacts(text: str) -> Dict[str, List[str]]:
return {
"emails" : re.findall(r'[\w.+-]+@[\w-]+\.[a-z]{2,}', text, re.I),
"phones" : re.findall(r'\b0[5-7]\d{8}\b', text),
}
sample = """
للتواصل: admin@website.ma أو support@help.com
هاتف: 0661234567 أو 0522345678
البريد الثانوي: info@test.org
"""
result = extract_contacts(sample)
print(f"إيميلات ({len(result['emails'])}):")
for e in result['emails']:
print(f" 📧 {e}")
print(f"\nهواتف ({len(result['phones'])}):")
for p in result['phones']:
print(f" 📱 {p}")
🚀 المشروع — أداة تنظيف وتحليل النصوص
Python — text_analyzer.py
import re
from collections import Counter
class TextAnalyzer:
"""أداة تحليل وتنظيف النصوص باستخدام Regex"""
def __init__(self, text: str):
self.text = text
def extract_emails(self) -> list:
return re.findall(r'[\w.+-]+@[\w-]+\.[a-z]{2,}', self.text, re.I)
def extract_urls(self) -> list:
return re.findall(r'https?://\S+', self.text)
def extract_numbers(self) -> list:
return [float(n) if '.' in n else int(n)
for n in re.findall(r'\b\d+(?:\.\d+)?\b', self.text)]
def word_frequency(self, top=5) -> list:
words = re.findall(r'\b[a-zأ-ي]{3,}\b', self.text, re.I)
return Counter(words).most_common(top)
def clean(self) -> str:
t = re.sub(r'<[^>]+>', '', self.text) # إزالة HTML
t = re.sub(r'https?://\S+', '[رابط]', t) # استبدال URLs
t = re.sub(r'\s{2,}', ' ', t) # إزالة مسافات زائدة
return t.strip()
def report(self):
print("="*40)
print(" 📊 تقرير التحليل")
print("="*40)
print(f"📧 إيميلات : {self.extract_emails()}")
print(f"🔗 روابط : {self.extract_urls()}")
print(f"🔢 أرقام : {self.extract_numbers()}")
print(f"📝 أكثر كلمات : {self.word_frequency()}")
print(f"\n✨ نص نظيف:\n{self.clean()}")
# ---- اختبار ----
sample = """
<h1>مرحباً!</h1> تواصل معنا على contact@example.com
السعر 99.5 درهم. زر https://python.org لمزيد من المعلومات.
رقم التواصل: 0612345678 أو info@site.ma
"""
TextAnalyzer(sample).report()
🎉 أتقنت التعابير النمطية!
Regex هو أداة قوية ستستخدمها في كل مشروع حقيقي — من التحقق من المدخلات إلى تحليل الملفات الضخمة.
الدرس التالي: APIs و Web Scraping ←