التعبيرات النمطية في Rust مع أمثلة: عمليًا
\n\nالتعبيرات النمطية (regex) هي أداة قوية للعمل مع النصوص. في Rust، على الرغم من الكتابة الصارمة والتركيز على الأمان، فإن العمل مع التعبيرات النمطية يتم بكفاءة وسهولة بفضل crate regex. في هذه المقالة، سنشرح كيفية استخدام التعبيرات النمطية في Rust عمليًا: من التثبيت إلى سيناريوهات معالجة السلاسل النصية المعقدة.
سنستعرض العمليات الأساسية: البحث، الاستخراج، الاستبدال، والتحقق من الصحة. جميع أمثلة الكود مكتوبة بلغة Rust وجاهزة للاستخدام.
\n\nتثبيت وإضافة crate regex
\n\nللعمل مع التعبيرات النمطية في Rust، يتم استخدام crate خارجي ولكنه شائع جدًا وهو regex. هذا crate غير مضمن في المكتبة القياسية، لذا يجب إضافته إلى المشروع.
أضف التبعية إلى ملف Cargo.toml:
[dependencies]\nregex = "1"\n\nبعد ذلك، يمكن استيراد الأنواع الأساسية في الكود:
\n\nuse regex::Regex;\n\nيقوم crate regex بترجمة التعبير النمطي إلى آلة حالة محدودة، مما يوفر أداءً عاليًا. يتم اكتشاف أخطاء ترجمة النمط في وقت التشغيل (عبر Result)، مما يجعل الكود موثوقًا.
الأساسيات: البحث عن التطابقات في سلسلة نصية
\n\nأبسط طريقة للتحقق مما إذا كانت السلسلة النصية تطابق نمطًا ما هي استخدام طريقة is_match. لنلقِ نظرة على مثال للتحقق من وجود عنوان بريد إلكتروني في النص.
use regex::Regex;\n\nfn main() {\n let re = Regex::new(r"\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b").unwrap();\n let text = "تواصل معنا على البريد الإلكتروني support@example.com.";\n\n if re.is_match(text) {\n println!("تم العثور على البريد الإلكتروني!");\n } else {\n println!("لم يتم العثور على البريد الإلكتروني.");\n }\n}\n\nتأخذ طريقة Regex::new سلسلة نصية تحتوي على النمط وتعيد Result. يتم استخدام unwrap() هنا للتبسيط، ولكن في كود الإنتاج يجب معالجة الأخطاء بشكل صحيح.
للبحث عن جميع التطابقات، يتم استخدام طريقة find_iter التي تعيد مكررًا عبر كائنات Match:
use regex::Regex;\n\nfn main() {\n let re = Regex::new(r"\\d+").unwrap();\n let text = "الأسعار: 100 و 250 و 500 روبل.";\n\n for mat in re.find_iter(text) {\n println!("تم العثور على رقم: {}", mat.as_str());\n }\n}\n\nسيقوم هذا الكود بطباعة:
\n\nتم العثور على رقم: 100\nتم العثور على رقم: 250\nتم العثور على رقم: 500\n\nاستخراج البيانات باستخدام مجموعات الالتقاط
\n\nتسمح مجموعات الالتقاط (capturing groups) باستخراج ليس فقط التطابق الكامل، بل أجزاء منه. على سبيل المثال، تحليل تاريخ بتنسيق "يوم-شهر-سنة".
\n\nuse regex::Regex;\n\nfn main() {\n let re = Regex::new(r"(\\d{2})-(\\d{2})-(\\d{4})").unwrap();\n let text = "اليوم 15-03-2024.";\n\n if let Some(caps) = re.captures(text) {\n println!("اليوم: {}", &caps[1]);\n println!("الشهر: {}", &caps[2]);\n println!("السنة: {}", &caps[3]);\n }\n}\n\nيبدأ ترقيم المجموعات من 1 (المجموعة 0 هي التطابق الكامل). تعيد طريقة captures Option<Captures>. إذا تم العثور على تطابق، يمكننا الوصول إلى المجموعات عبر الفهرس.
لاستخراج جميع التكرارات مع المجموعات، يتم استخدام captures_iter:
use regex::Regex;\n\nfn main() {\n let re = Regex::new(r"(\\w+)@(\\w+)\\.(\\w+)").unwrap();\n let text = "البريد الإلكتروني: user@host.com, admin@site.org";\n\n for caps in re.captures_iter(text) {\n println!("اسم المستخدم: {}, المضيف: {}, النطاق: {}", &caps[1], &caps[2], &caps[3]);\n }\n}\n\nهذا الأسلوب لا غنى عنه عند تحليل السجلات وملفات التكوين أو استخراج البيانات المنظمة من النص غير المنظم.
\n\nاستبدال النص وفقًا لنمط
\n\nيوفر crate regex طرقًا لاستبدال النص. على سبيل المثال، إخفاء أرقام بطاقات الائتمان:
use regex::Regex;\n\nfn main() {\n let re = Regex::new(r"\\d{4}-\\d{4}-\\d{4}-\\d{4}").unwrap();\n let text = "بطاقتي: 1234-5678-9012-3456.";\n let result = re.replace_all(text, "****-****-****-****");\n println!("{}", result);\n}\n\nسيقوم هذا الكود بطباعة:
\n\nبطاقتي: ****-****-****-****.\n\nيمكن أيضًا استخدام المراجع الخلفية (backreferences) في سلسلة الاستبدال. على سبيل المثال، تغيير تنسيق التاريخ من "يوم-شهر-سنة" إلى "سنة/شهر/يوم":
\n\nuse regex::Regex;\n\nfn main() {\n let re = Regex::new(r"(\\d{2})-(\\d{2})-(\\d{4})").unwrap();\n let text = "التاريخ: 15-03-2024.";\n let result = re.replace_all(text, "$3/$2/$1");\n println!("{}", result);\n}\n\nالنتيجة:
\n\nالتاريخ: 2024/03/15.\n\nطريقة replace تستبدل التطابق الأول فقط، بينما replace_all تستبدل جميع التطابقات.
التحقق من صحة البيانات (Validation)
\n\nغالبًا ما تُستخدم التعبيرات النمطية للتحقق من صحة إدخال المستخدم. على سبيل المثال، التحقق من صحة رقم الهاتف:
\n\nuse regex::Regex;\n\nfn validate_phone(phone: &str) -> bool {\n let re = Regex::new(r"^\\+?[1-9]\\d{1,14}$").unwrap();\n re.is_match(phone)\n}\n\nfn main() {\n let phones = vec!["+71234567890", "12345", "+7 (123) 456-78-90"];\n for phone in phones {\n println!("{}: {}", phone, validate_phone(phone));\n }\n}\n\nهذا الكود سيطبع:
\n\n+71234567890: true\n12345: false\n+7 (123) 456-78-90: false\n\nلاحظ أن التعبير النمطي لا يتحقق من صحة الأقواس والشرطات - فهو يقبل فقط الأرقام وعلامة "+" في البداية. للتحقق من الصيغ المعقدة، يجب استخدام تعبيرات نمطية أكثر دقة.
\n\nنصائح لتحسين الأداء
\n\nعند العمل مع كميات كبيرة من النصوص، يجب مراعاة ما يلي:
\n\n- \n
- تجميع التعبير النمطي مرة واحدة: أنشئ كائن
Regexخارج الحلقة وأعد استخدامه. \n - تجنب التعبيرات النمطية المعقدة بشكل مفرط: في بعض الأحيان، يكون التحليل اليدوي للسلسلة النصية أسرع. \n
- استخدم
RegexSet: إذا كنت بحاجة إلى التحقق من تطابق سلسلة نصية مع عدة تعبيرات نمطية في وقت واحد، استخدمRegexSetمن crateregex. \n - انتبه إلى التجميع غير الالتقاطي: استخدم
(?:...)بدلاً من(...)إذا لم تكن بحاجة إلى حفظ المجموعة. \n
الخلاصة
\n\nالتعبيرات النمطية في Rust هي أداة قوية ومرنة لمعالجة النصوص. بفضل crate regex، يمكنك بسهولة البحث عن البيانات واستخراجها واستبدالها والتحقق من صحتها. على الرغم من أن Rust لغة صارمة، إلا أن العمل مع التعبيرات النمطية فيها لا يختلف كثيرًا عن اللغات الأخرى، مع ميزة إضافية تتمثل في الأمان والأداء العالي.
للاستخدام اليومي، يكفي حفظ الطرق الأساسية: is_match و find_iter و captures و replace. بالنسبة للمشاريع الأكثر تعقيدًا، ادرس وثائق crate regex - ستجد هناك العديد من الميزات المفيدة.