مقدمة: لماذا نحتاج التعبيرات المنتظمة في Rust؟
التعبيرات المنتظمة (regex) هي أداة قوية للبحث عن النص واستخراجه واستبداله وفقًا لنمط معين. في Rust، يتم العمل مع التعبيرات المنتظمة من خلال crate regex، والذي يُعتبر من أسرع وأكثرها أمانًا في النظام البيئي. على عكس العديد من اللغات الأخرى، يقوم مترجم Rust بالتحقق من صحة التعبير المنتظم في مرحلة التجميع (إذا تم استخدام الماكرو regex!)، مما يساعد على تجنب الأخطاء أثناء التنفيذ.
في هذه المقالة، سنستعرض أمثلة عملية لاستخدام التعبيرات المنتظمة في Rust: من البحث الأساسي إلى استخراج البيانات المعقد. ستتعلم كيفية إعداد المشروع، وكتابة الأنماط، ومعالجة النتائج.
1. إعداد المشروع وأول regex
إضافة التبعية
للبدء، أضف crate regex إلى ملف Cargo.toml:
[dependencies]regex = "1"بعد ذلك، قم بتحديث التبعيات باستخدام الأمر cargo build.
المثال الأول: البحث عن كلمة
لنكتب برنامجًا بسيطًا يبحث عن كلمة "Rust" في سلسلة نصية:
use regex::Regex;
fn main() { let re = Regex::new(r"Rust").unwrap(); let text = "أنا أتعلم Rust و Python."; if re.is_match(text) { println!("تم العثور على كلمة 'Rust'!"); } else { println!("لم يتم العثور على الكلمة."); }}تقوم الدالة Regex::new بتجميع النمط. إذا كان النمط غير صالح، سينتهي البرنامج بخطأ (استخدم unwrap() فقط في النماذج الأولية).
2. استخراج البيانات باستخدام الالتقاطات
التجميع والمجموعات المسماة
تسمح التعبيرات المنتظمة باستخراج أجزاء من النص باستخدام الأقواس المستديرة. لنلق نظرة على مثال مع البريد الإلكتروني:
use regex::Regex;
fn main() { let re = Regex::new(r"(?P<name>[a-zA-Z0-9._%+-]+)@(?P<domain>[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})").unwrap(); let text = "بريدي الإلكتروني: user@example.com، وأيضًا admin@test.org."; for caps in re.captures_iter(text) { println!("الاسم: {}", &caps["name"]); println!("النطاق: {}", &caps["domain"]); }}في هذا المثال، نستخدم المجموعات المسماة (?P<name>...). وهذا يجعل الكود أكثر قابلية للقراءة وأسهل في المعالجة اللاحقة.
استخراج جميع التطابقات
إذا كنت بحاجة إلى الحصول على جميع التطابقات بدون مجموعات، استخدم find_iter:
use regex::Regex;
fn main() { let re = Regex::new(r"\\d+").unwrap(); let text = "الأسعار: 100 و 250 و 500 روبل."; for mat in re.find_iter(text) { println!("تم العثور على رقم: {}", mat.as_str()); }}يقوم الأسلوب find_iter بإرجاع مكرر على كائنات Match، والتي تحتوي على مواضع البداية والنهاية، بالإضافة إلى نص التطابق نفسه.
3. استبدال النص والأنماط المعقدة
استبدال بسيط
لنستبدل جميع الأرقام بكلمة "NUMBER":
use regex::Regex;
fn main() { let re = Regex::new(r"\\d+").unwrap(); let text = "الكود: 12345، الحالة: 0."; let result = re.replace_all(text, "NUMBER"); println!("{}", result); // "الكود: NUMBER، الحالة: NUMBER."}استبدال باستخدام الالتقاطات
يمكنك الإشارة إلى المجموعات في سلسلة الاستبدال باستخدام $1 و $2 أو المجموعات المسماة $name:
use regex::Regex;
fn main() { let re = Regex::new(r"(?P<year>\\d{4})-(?P<month>\\d{2})-(?P<day>\\d{2})").unwrap(); let text = "التاريخ: 2024-03-15"; let result = re.replace_all(text, "$day.$month.$year"); println!("{}", result); // "التاريخ: 15.03.2024"}نمط معقد: تحليل السجلات
لنلق نظرة على مثال واقعي — تحليل سطر سجل من خادم ويب:
use regex::Regex;
fn main() { let log_line =