۱۲
عبارات با قاعده (Regex) 🔍
یاد میگیریم چطور با الگوهای قدرتمند، متنها رو جستجو و پردازش کنیم!
Regex چیه و چرا مهمه؟
عبارات منظم (Regular Expressions) ابزاری قدرتمند برای جستجو، تطبیق و دستکاری متنهاست. مثل یه زبان خاص برای توصیف الگوهای متنی:
🎯 کاربردهای Regex
- اعتبارسنجی: بررسی فرمت ایمیل، شماره تلفن، کد ملی
- استخراج داده: پیدا کردن اطلاعات خاص از متن
- جایگزینی: تغییر الگوهای خاص در متن
- تقسیم متن: جدا کردن متن بر اساس الگو
اولین تجربه با Regex
import re
# متن نمونه
text = """سلام! من علی هستم. ایمیل من ali@gmail.com است.
شماره تلفن من 09123456789 است.
سایت من https://example.com است."""
print("=== جستجوی ساده ===")
# جستجوی کلمه "علی"
result = re.search(r"علی", text)
if result:
print(f"کلمه 'علی' در موقعیت {result.start()} پیدا شد")
# پیدا کردن همه اعداد
numbers = re.findall(r"\d+", text)
print(f"اعداد پیدا شده: {numbers}")
# پیدا کردن ایمیل
email_pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
email = re.search(email_pattern, text)
if email:
print(f"ایمیل پیدا شده: {email.group()}")
# پیدا کردن شماره موبایل
phone_pattern = r"09\d{9}"
phone = re.search(phone_pattern, text)
if phone:
print(f"شماره موبایل: {phone.group()}")
# پیدا کردن URL
url_pattern = r"https?://[^\s]+"
url = re.search(url_pattern, text)
if url:
print(f"آدرس سایت: {url.group()}")
# خروجی:
# کلمه 'علی' در موقعیت 11 پیدا شد
# اعداد پیدا شده: ['09123456789']
# ایمیل پیدا شده: ali@gmail.com
# شماره موبایل: 09123456789
# آدرس سایت: https://example.com
الگوهای پایه Regex
بیایید با کاراکترهای خاص و الگوهای پایه آشنا شویم:
📝 کاراکترهای خاص
. - هر کاراکتر (به جز خط جدید)
* - صفر یا بیشتر
+ - یک یا بیشتر
? - صفر یا یک
^ - شروع رشته
$ - پایان رشته
\d - رقم (0-9)
\w - حرف، رقم، _
\s - فاصله، تب، خط جدید
\D - غیر رقم
\W - غیر حرف
\S - غیر فاصله
مثالهای عملی الگوها
import re
# متن نمونه برای تست
test_text = """نام: احمد رضایی
سن: 25 سال
ایمیل: ahmad.rezaei@example.com
تلفن: 021-12345678
موبایل: 09123456789
کد پستی: 1234567890
تاریخ: 1402/05/15
ساعت: 14:30:25"""
print("=== الگوهای مختلف ===")
# 1. پیدا کردن اعداد
digits = re.findall(r"\d+", test_text)
print(f"اعداد: {digits}")
# 2. پیدا کردن کلمات فارسی
farsi_words = re.findall(r"[آ-ی]+", test_text)
print(f"کلمات فارسی: {farsi_words}")
# 3. پیدا کردن ایمیل با گروهبندی
email_pattern = r"([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,})"
email_match = re.search(email_pattern, test_text)
if email_match:
print(f"نام کاربری: {email_match.group(1)}")
print(f"دامنه: {email_match.group(2)}")
print(f"پسوند: {email_match.group(3)}")
# 4. پیدا کردن تاریخ شمسی
date_pattern = r"(\d{4})/(\d{2})/(\d{2})"
date_match = re.search(date_pattern, test_text)
if date_match:
year, month, day = date_match.groups()
print(f"سال: {year}, ماه: {month}, روز: {day}")
# 5. پیدا کردن ساعت
time_pattern = r"(\d{2}):(\d{2}):(\d{2})"
time_match = re.search(time_pattern, test_text)
if time_match:
hour, minute, second = time_match.groups()
print(f"ساعت: {hour}, دقیقه: {minute}, ثانیه: {second}")
# 6. استفاده از کلاس کاراکتر
phone_numbers = re.findall(r"[0-9]{3}-[0-9]{8}|09[0-9]{9}", test_text)
print(f"شماره تلفنها: {phone_numbers}")
# 7. استفاده از quantifiers
postal_code = re.search(r"\d{10}", test_text)
if postal_code:
print(f"کد پستی: {postal_code.group()}")
توابع مهم ماژول re
ماژول re توابع مختلفی برای کار با regex داره:
توابع اصلی ماژول re
import re
# متن نمونه
text = "سلام دوستان! امروز 1402/05/15 است. ایمیل من test@example.com است."
print("=== توابع مختلف re ===")
# 1. re.search() - اولین تطبیق
result = re.search(r"\d{4}/\d{2}/\d{2}", text)
if result:
print(f"تاریخ پیدا شده: {result.group()}")
print(f"موقعیت: {result.start()}-{result.end()}")
# 2. re.match() - تطبیق از ابتدای رشته
match_result = re.match(r"سلام", text)
if match_result:
print(f"شروع با 'سلام': {match_result.group()}")
# 3. re.findall() - همه تطبیقها
all_numbers = re.findall(r"\d+", text)
print(f"همه اعداد: {all_numbers}")
# 4. re.finditer() - iterator برای تطبیقها
for match in re.finditer(r"\w+@\w+\.\w+", text):
print(f"ایمیل: {match.group()} در موقعیت {match.start()}")
# 5. re.sub() - جایگزینی
new_text = re.sub(r"\d{4}/\d{2}/\d{2}", "[تاریخ مخفی]", text)
print(f"متن جدید: {new_text}")
# 6. re.split() - تقسیم رشته
parts = re.split(r"[.!?]", text)
print(f"بخشهای جمله: {[part.strip() for part in parts if part.strip()]}")
# 7. re.compile() - کامپایل الگو برای استفاده مکرر
pattern = re.compile(r"\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b")
emails = pattern.findall(text)
print(f"ایمیلهای پیدا شده: {emails}")
# 8. استفاده از flags
text_mixed = "Hello سلام WORLD دنیا"
words = re.findall(r"[a-z]+", text_mixed, re.IGNORECASE)
print(f"کلمات انگلیسی: {words}")
# 9. گروهبندی با نام
phone_pattern = r"(?P\d{3})-(?P\d{8})"
phone_text = "تلفن: 021-12345678"
phone_match = re.search(phone_pattern, phone_text)
if phone_match:
print(f"کد شهر: {phone_match.group('area')}")
print(f"شماره: {phone_match.group('number')}")
پروژه عملی: اعتبارسنج دادهها
بیایید یک کلاس برای اعتبارسنجی انواع دادهها بسازیم:
کلاس DataValidator
import re
from typing import List, Dict, Optional
class DataValidator:
"""کلاس اعتبارسنجی دادهها با Regex"""
def __init__(self):
# الگوهای مختلف
self.patterns = {
'email': r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
'iranian_mobile': r'^09[0-9]{9}$',
'iranian_phone': r'^0[1-9][0-9]{1,2}-?[0-9]{8}$',
'iranian_postal_code': r'^[0-9]{10}$',
'iranian_national_id': r'^[0-9]{10}$',
'url': r'^https?://[^\s/$.?#].[^\s]*$',
'ip_address': r'^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$',
'persian_text': r'^[آ-ی\s]+$',
'english_text': r'^[a-zA-Z\s]+$',
'strong_password': r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'
}
self.validation_history = []
def validate_email(self, email: str) -> bool:
"""اعتبارسنجی ایمیل"""
is_valid = bool(re.match(self.patterns['email'], email))
self.validation_history.append({
'type': 'email',
'value': email,
'valid': is_valid
})
return is_valid
def validate_iranian_mobile(self, mobile: str) -> bool:
"""اعتبارسنجی شماره موبایل ایرانی"""
is_valid = bool(re.match(self.patterns['iranian_mobile'], mobile))
self.validation_history.append({
'type': 'iranian_mobile',
'value': mobile,
'valid': is_valid
})
return is_valid
def validate_iranian_national_id(self, national_id: str) -> bool:
"""اعتبارسنجی کد ملی ایرانی"""
# ابتدا فرمت را بررسی میکنیم
if not re.match(self.patterns['iranian_national_id'], national_id):
return False
# الگوریتم محاسبه رقم کنترل
check_sum = sum(int(national_id[i]) * (10 - i) for i in range(9))
remainder = check_sum % 11
if remainder < 2:
expected_check_digit = remainder
else:
expected_check_digit = 11 - remainder
is_valid = int(national_id[9]) == expected_check_digit
self.validation_history.append({
'type': 'iranian_national_id',
'value': national_id,
'valid': is_valid
})
return is_valid
def extract_data_from_text(self, text: str) -> Dict[str, List[str]]:
"""استخراج انواع داده از متن"""
extracted = {
'emails': re.findall(self.patterns['email'], text),
'mobiles': re.findall(self.patterns['iranian_mobile'], text),
'urls': re.findall(self.patterns['url'], text),
'numbers': re.findall(r'\d+', text)
}
return extracted
def clean_phone_number(self, phone: str) -> str:
"""تمیز کردن شماره تلفن"""
# حذف کاراکترهای غیرضروری
cleaned = re.sub(r'[^0-9]', '', phone)
# فرمت استاندارد
if len(cleaned) == 11 and cleaned.startswith('09'):
return cleaned
elif len(cleaned) == 10 and cleaned.startswith('9'):
return '0' + cleaned
else:
return cleaned
def mask_sensitive_data(self, text: str) -> str:
"""پنهان کردن اطلاعات حساس"""
# پنهان کردن ایمیل
text = re.sub(r'([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})',
r'***@\2', text)
# پنهان کردن شماره موبایل
text = re.sub(r'(09)(\d{2})(\d{3})(\d{4})',
r'\1**\3****', text)
# پنهان کردن کد ملی
text = re.sub(r'\b(\d{3})(\d{4})(\d{3})\b',
r'\1****\3', text)
return text
def get_validation_report(self) -> Dict:
"""گزارش اعتبارسنجیها"""
total = len(self.validation_history)
valid_count = sum(1 for item in self.validation_history if item['valid'])
return {
'total_validations': total,
'valid_count': valid_count,
'invalid_count': total - valid_count,
'success_rate': (valid_count / total * 100) if total > 0 else 0,
'history': self.validation_history
}
# تست کلاس
if __name__ == "__main__":
validator = DataValidator()
print("=== تست اعتبارسنجی ===")
# تست ایمیل
emails = ['test@example.com', 'invalid-email', 'user@domain.co.uk']
for email in emails:
result = validator.validate_email(email)
print(f"ایمیل {email}: {'معتبر' if result else 'نامعتبر'}")
# تست موبایل
mobiles = ['09123456789', '9123456789', '021123456']
for mobile in mobiles:
result = validator.validate_iranian_mobile(mobile)
print(f"موبایل {mobile}: {'معتبر' if result else 'نامعتبر'}")
# تست کد ملی
national_ids = ['0123456789', '1234567890']
for nid in national_ids:
result = validator.validate_iranian_national_id(nid)
print(f"کد ملی {nid}: {'معتبر' if result else 'نامعتبر'}")
# استخراج داده از متن
sample_text = """سلام! ایمیل من ali@example.com است.
شماره موبایل: 09123456789
سایت: https://example.com
"""
extracted = validator.extract_data_from_text(sample_text)
print(f"\nدادههای استخراج شده: {extracted}")
# پنهان کردن اطلاعات حساس
masked = validator.mask_sensitive_data(sample_text)
print(f"\nمتن با اطلاعات پنهان:\n{masked}")
# گزارش
report = validator.get_validation_report()
print(f"\nگزارش: {report['valid_count']} از {report['total_validations']} معتبر")
تمرین عملی
یک برنامه بنویسید که:
- از کاربر یک متن دریافت کند
- همه ایمیلها، شماره تلفنها و URL های موجود در متن را پیدا کند
- اعتبار هر کدام را بررسی کند
- گزارش کاملی از یافتهها ارائه دهد
راه حل تمرین
import re
class TextAnalyzer:
def __init__(self):
self.patterns = {
'email': r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
'phone': r'\b(?:09\d{9}|0[1-9]\d{1,2}-?\d{8})\b',
'url': r'https?://[^\s]+'
}
def analyze_text(self, text):
results = {
'emails': [],
'phones': [],
'urls': []
}
# پیدا کردن ایمیلها
emails = re.findall(self.patterns['email'], text)
for email in emails:
results['emails'].append({
'value': email,
'valid': self.validate_email(email)
})
# پیدا کردن شماره تلفنها
phones = re.findall(self.patterns['phone'], text)
for phone in phones:
results['phones'].append({
'value': phone,
'valid': self.validate_phone(phone)
})
# پیدا کردن URL ها
urls = re.findall(self.patterns['url'], text)
for url in urls:
results['urls'].append({
'value': url,
'valid': self.validate_url(url)
})
return results
def validate_email(self, email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
def validate_phone(self, phone):
# حذف خط تیره
clean_phone = phone.replace('-', '')
return len(clean_phone) >= 10
def validate_url(self, url):
return url.startswith(('http://', 'https://'))
def generate_report(self, results):
print("=== گزارش تحلیل متن ===")
for data_type, items in results.items():
print(f"\n{data_type.upper()}:")
if not items:
print(" هیچ موردی پیدا نشد")
else:
for item in items:
status = "✅ معتبر" if item['valid'] else "❌ نامعتبر"
print(f" {item['value']} - {status}")
# استفاده
analyzer = TextAnalyzer()
sample_text = input("متن خود را وارد کنید: ")
results = analyzer.analyze_text(sample_text)
analyzer.generate_report(results)
چالش پیشرفته
یک سیستم پردازش لاگ بسازید که:
- لاگهای وب سرور را تجزیه کند
- IP آدرسها، تاریخ/زمان، HTTP method و status code را استخراج کند
- آمار کاملی از درخواستها ارائه دهد
- IP های مشکوک (تعداد درخواست بالا) را شناسایی کند
این چالش نیاز به ترکیب regex با ساختارهای داده پیشرفته دارد. سعی کنید خودتان حل کنید!