FlowingDev

شرح Regex: أداة البحث والاستبدال الخارقة

تعلم أساسيات التعابير النمطية (regex)، اللغة المصغرة القوية لمطابقة الأنماط النصية والبحث فيها والتعديل عليها.

جرّب الأداة: مختبر التعابير النمطية

في جملة واحدة

التعابير النمطية (أو "regex") هي سلسلة خاصة من الحروف تحدد نمط بحث، تسمح لك بالعثور على النصوص واستبدالها والتحقق من صحتها بدقة جراحية.

المشكلة التي تحلها

تخيل هذا السيناريو: لديك ملف سجلات (log file) عملاق، وتحتاج إلى العثور على كل رسائل الخطأ التي جاءت من عنوان IP محدد في الساعة الأخيرة. البحث البسيط عن كلمة "error" سيعطيك سيلاً جارفاً من المعلومات غير المفيدة. يمكنك كتابة سكربت يحتوي على الكثير من جمل if ومنطق لتقسيم النصوص، لكن هذه الطريقة هشة، وبطيئة في كتابتها، ومؤلمة في تصحيح أخطائها.

هذا هو الحساء البدائي الذي انبثق منه الـ regex. في الماضي، كان رواد يونكس (Unix) مثل كين تومبسون بحاجة إلى طريقة أفضل للتعامل مع النصوص. كانوا يبنون أدوات مثل grep (اختصار لـ Global Regular Expression Print) ومحرر النصوص ed. مجرد ضغط Ctrl+F لم يكن ليجدي نفعاً. كانوا بحاجة إلى لغة لـ وصف النص الذي يبحثون عنه، وليس فقط النص الحرفي نفسه.

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

كيف تعمل من الداخل

يبدو التعبير النمطي وكأنه خليط فوضوي من الرموز، لكنه في الواقع برنامج مصغر منظم للغاية. هناك برنامج خاص يسمى "محرك regex" يقرأ النمط الذي كتبته ويستخدمه لمسح النص المُدخل. دعنا نفك هذه التعويذة السحرية.

اللبنات الأساسية: الحروف الحرفية (Literals) والحروف الخاصة (Metacharacters)

في جوهره، يتكون نمط الـ regex من نوعين من الحروف:

  1. الحروف الحرفية (Literals): هي مجرد حروف عادية تطابق نفسها. النمط cat سيجد التسلسل الدقيق للحروف "c" و "a" و "t". سهل جداً.
  2. الحروف الخاصة (Metacharacters): هذه هي الخلطة السرية. هي لا تطابق نفسها؛ بل لديها قوة خارقة. النقطة (.) هي مثال كلاسيكي. إنها حرف بدل (wildcard) يطابق أي حرف واحد (عادةً باستثناء سطر جديد). لذا، c.t ستطابق "cat" و "cot" و "c_t" وحتى "c!t".

من بين اللاعبين النجوم الآخرين * (طابق الشيء السابق 0 مرة أو أكثر)، + (مرة واحدة أو أكثر)، و ? (0 أو مرة واحدة). تسمى هذه محددات الكمية (quantifiers).

فئات الحروف والاختصارات

ماذا لو أردت مطابقة أي حرف علة (vowel)؟ يمكنك كتابة (a|e|i|o|u)، لكن هذا غير عملي. بدلاً من ذلك، يمكنك استخدام فئة حروف (character class): [aeiou]. الأقواس المربعة تتيح لك تحديد مجموعة خاصة بك من الحروف المسموح بها.

الأمر يصبح أفضل مع النطاقات. هل تريد مطابقة أي حرف صغير؟ [a-z]. أي رقم؟ [0-9].

لتوفير المزيد من الكتابة، لدى الـ regex اختصارات للفئات الشائعة:

  • \d: أي رقم ([0-9])
  • \w: أي حرف "كلمة" (حروف، أرقام، وشرطة سفلية) ([a-zA-Z0-9_])
  • \s: أي حرف مسافة بيضاء (مسافة، تاب، سطر جديد)
  • \D، \W، \S: العكس! تطابق أي شيء ليس رقمًا، أو حرف كلمة، أو مسافة بيضاء، على التوالي.

محددات الكمية: كم مرة؟

لقد التقينا بـ * و + و ? سابقًا. تخبر هذه الرموز المحرك كم مرة يجب أن يطابق الحرف أو المجموعة السابقة.

محدد الكمية المعنى مثال يطابق
? صفر أو مرة واحدة colou?r "color", "colour"
* صفر أو أكثر من المرات goa*l "gl", "gol", "goooal"
+ مرة واحدة أو أكثر goa+l "goal", "goooal"
{n} بالضبط n من المرات \d{4} "1984"
{n,} n من المرات أو أكثر \w{3,} "cat", "tiger"
{n,m} بين n و m من المرات [a-z]{5,7} "regex", "pattern"

تفصيل مهم هو أن محددات الكمية هذه "شَرِهة" (greedy) بشكل افتراضي. ستحاول مطابقة أكبر قدر ممكن من النص. إذا كان لديك النص <p>first</p><p>second</p> والنمط /<p>.*</p>/، فإن .* الشره سيطابق كل شيء من أول <p> إلى آخر </p>. لجعله "كسولاً" (lazy) (ليطابق أقصر سلسلة ممكنة)، أضف ?: /<p>.*?</p>/. الآن سيطابق كل وسم <p>...</p> على حدة.

المثبتات (Anchors) والحدود

المثبتات لا تطابق حروفا؛ بل تطابق مواضع.

  • ^: تؤكد الموضع في بداية السلسلة النصية (أو السطر، في وضع الأسطر المتعددة). ^cat تطابق "cat" فقط إذا كانت في البداية تمامًا.
  • $: تؤكد الموضع في نهاية السلسلة النصية (أو السطر). cat$ تطابق "cat" فقط إذا كانت في النهاية تمامًا.
  • \b: تؤكد "حد الكلمة" (word boundary)—الموضع بين حرف كلمة (\w) وحرف ليس من كلمة (\W). النمط \bcat\b سيطابق "cat" في "the cat sat" ولكنه لن يطابقها في "concatenate". هذا مفيد بشكل لا يصدق لمطابقة كلمات كاملة.

التجميع والالتقاط

الأقواس () تفعل شيئين:

  1. التجميع: تجمع جزءًا من النمط حتى تتمكن من تطبيق محدد كمية عليه. (ha)+ ستطابق "ha" و "haha" و "hahaha" وهكذا.
  2. الالتقاط: "تلتقط" النص الذي تمت مطابقته بداخلها. هذه قوة خارقة. إذا طابقت النص "ID: 12345" مع النمط ID: (\d+)، فلن يخبرك المحرك فقط بأنه وجد تطابقًا، بل سيسلمك أيضًا السلسلة الملتقطة "12345". يمكنك بعد ذلك استخدام هذه المجموعات الملتقطة (التي غالبًا ما يشار إليها بـ $1، $2، إلخ، أو \1، \2) في عملية استبدال أو استخلاصها للمعالجة.

محرك Regex: بين NFA و DFA

هذه معلومة للمتقدمين، لكنها تشرح لماذا يمكن أن تكون بعض تعابير الـ regex بطيئة بشكل كارثي. معظم المحركات التي تستخدمها (في JavaScript، Python، Perl، Java) تعتمد على ما يسمى "آلة الحالات المحدودة غير الحتمية" (Nondeterministic Finite Automaton أو NFA). تعمل هذه المحركات بتجربة كل المسارات الممكنة عبر النمط. هذا قوي لأنه يسمح بميزات متقدمة مثل "المراجع الخلفية" (backreferences) (مطابقة نفس النص الذي تم التقاطه بواسطة مجموعة سابقًا). ومع ذلك، يمكن أن يؤدي أيضًا إلى عدد هائل من الخطوات، وهي مشكلة تسمى "التراجع الكارثي" (catastrophic backtracking)، حيث يمكن لنمط مكتوب بشكل سيء على نص صعب أن يتسبب في تعليق تطبيقك.

الأدوات الأقدم (وبعض الأدوات الحديثة المتخصصة مثل RE2 من Google) تستخدم "آلة الحالات المحدودة الحتمية" (Deterministic Finite Automaton أو DFA). محركات DFA أسرع بكثير ولا يمكن أن تعلق في حلقات التراجع، لكنها أقل تعبيرية ولا تدعم جميع الميزات الفاخرة لـ NFA.

قصص من الواقع

محقق ملفات السجلات

بدأ خادم ويب في إظهار أخطاء 500 عشوائية، وكان فريق DevOps في حالة تخبط. كانت ملفات السجلات عبارة عن سيل من الجيجابايت من سجلات الوصول الروتينية الممزوجة برسائل الخطأ الحرجة. استخدام grep يدويًا لم يوصلهم إلى أي مكان بسرعة. مطورة مبتدئة، متذكرةً درس علوم الكمبيوتر، قامت بكتابة regex على عجل: ^\[.*?\] \[error\].*?client: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}). قفز هذا النمط مباشرة إلى الأسطر التي تبدأ بطابع زمني ^\[.*?\]، وتحتوي على [error], ثم التقط عنوان IP للعميل. في ثوانٍ، حصلوا على قائمة من عناوين IP قليلة كانت جميعها تسبب الخطأ. تبين أنه كان جامع بيانات ويب (web scraper) معطوب يضرب نقطة نهاية API معينة بشكل متكرر.

الدرس: يمكن للـ regex أن يجد على الفور إبرة في كومة قش من البيانات النصية، محولاً مشكلة ساحقة إلى تحقيق مستهدف.

عملية الإنقاذ الكبرى لإعادة الهيكلة

قررت شركة ناشئة إعادة تسمية مفهوم أساسي في قاعدة شفرتها البرمجية. كان يجب إعادة تسمية الدالة create_legacy_widget() إلى build_standard_component() في كل مكان. البحث والاستبدال البسيط كان وصفة لكارثة—كان سيفوت حالات بمسافات مختلفة، والأسوأ من ذلك، قد يغير أشياء عن طريق الخطأ داخل التعليقات أو النصوص التوثيقية. استخدم مطور ميزة البحث والاستبدال باستخدام regex في محرره. بحث عن create_legacy_widget\s*\(\s*(\w+)\s*\) واستبدله بـ build_standard_component($1). تعامل النمط بذكاء مع المسافات البيضاء الاختيارية (\s*) والتقط الوسيط (argument) الذي تم تمريره إلى الدالة ((\w+))، معيدًا إدراجه ($1) في الاستبدال. تمت عملية إعادة الهيكلة الضخمة بأكملها بأمان في أقل من دقيقة.

الدرس: يتيح الـ regex إجراء تعديلات برمجية دقيقة ومدركة للسياق، وهو أمر مستحيل باستخدام أدوات البحث الأساسية.

حارس بوابات النماذج

كان مطور يبني نموذج تسجيل مستخدم جديد. كان لدى مدير المنتج قواعد محددة لأسماء المستخدمين: "من 3 إلى 15 حرفًا، حروف وأرقام وشرطات سفلية فقط." كانت المحاولة الأولية عبارة عن سلسلة من جمل if: تحقق من الطول، ثم قم بالمرور على السلسلة للتحقق من كل حرف. كانت الطريقة قبيحة وغير فعالة. تدخل مطور آخر واستبدل كتلة التعليمات البرمجية بأكملها بسطر واحد: if ( /^[a-zA-Z0-9_]{3,15}$/.test(username) ). ثبّت النمط ^...$ المطابقة على السلسلة بأكملها، مما يضمن عدم السماح بأي حروف شاردة، وفرض [a-zA-Z0-9_]{3,15} مجموعة الحروف المسموح بها وقواعد الطول دفعة واحدة.

الدرس: للتحقق من صحة البيانات، يعد الـ regex الطريقة الأكثر إيجازًا وقوة لتحديد وفرض قواعد التنسيق.

أخطاء وفخاخ شائعة

  • الشراهة ليست جيدة دائمًا. تذكر أن محددات الكمية مثل * و + شرهة. إذا كنت تحاول مطابقة وسوم HTML باستخدام <b>.*</b> على النص "Make it <b>bold</b> and <b>strong</b>"، فستطابق السلسلة بأكملها من أول <b> إلى آخر </b>. استخدم محدد الكمية الكسول *? لمطابقة أقصر نص ممكن: <b>.*?</b>.
  • نسيان تهريب (escape) الحروف الخاصة. إذا كنت تريد مطابقة نقطة . حرفية أو علامة زائد +، فيجب عليك تهريبها بشرطة مائلة عكسية: \.، \+. البحث عن 1+1 باستخدام النمط 1+1 سيفشل، لأن + هو محدد كمية. أنت بحاجة إلى 1\+1.
  • فخ "النقطة تطابق كل شيء". الحرف الخاص . هو حرف بدل قوي، لكنه افتراضيًا لا يطابق حروف السطر الجديد. هذا يمكن أن يوقعك في الخطأ عند تحليل نص متعدد الأسطر. معظم محركات regex لديها وضع "dotall" أو "single line" (غالبًا ما يتم تفعيله بواسطة علَم، مثل s) يجعل . يطابق الأسطر الجديدة أيضًا.
  • التراجع الكارثي (Catastrophic Backtracking). قد يبدو regex مثل (a+)+b بسيطًا، ولكن عند تشغيله على سلسلة مثل "aaaaaaaaaaaaaaaaaaaaaaaaaaac"، يمكن لمحرك NFA أن يضيع في عدد مذهل من الطرق لتجميع حروف a. هذا يمكن أن يجمد برنامجك. كن حذرًا من محددات الكمية المتداخلة، خاصة عندما يمكن للمجموعة الداخلية أن تطابق نفس النص بطرق متعددة.
  • الخلط بين المثبتات في وضع الأسطر المتعددة. عند تمكين وضع الأسطر المتعددة (العلم m)، يتغير معنى ^ و $. لم يعودا يطابقان البداية/النهاية المطلقة للسلسلة بأكملها، بل بداية/نهاية أي سطر. نسيان هذا يمكن أن يؤدي إلى مطابقات أو عدم مطابقات غير متوقعة.

لماذا يجب أن تكون على رادارك

يجب أن تفكر في الـ regex كلما واجهت مشكلة تتعلق بنص له بنية متوقعة. إنها ليست أداة لفهم معنى النص، ولكن لفهم نمطه. احتفظ بها في جعبتك من أجل:

  • التحقق من الصحة (Validation): هل هذا عنوان بريد إلكتروني صالح؟ رقم هاتف صالح؟ رمز لون ست عشري صالح؟ عنوان URL صالح؟ الـ regex هو الحارس الشخصي على باب بياناتك.
  • التحليل (Parsing): استخراج البيانات المنظمة من نصوص فوضوية وغير منظمة. فكر في كشط مواقع الويب، أو تحليل سجلات الخادم، أو معالجة التقارير.
  • تحويل الشفرة المصدرية: إجراء عمليات بحث واستبدال معقدة في قاعدة شفرتك البرمجية (codemods) أو ملفات التكوين.
  • التوجيه وإعادة الكتابة: تستخدم خوادم الويب مثل Nginx و Apache الـ regex بكثافة لإعادة كتابة عناوين URL وتوجيه الطلبات الواردة إلى الجزء الصحيح من تطبيقك.

تعلم الـ regex هو قوة خارقة للمطورين. إنها مهارة عابرة للمنصات ولا تعتمد على لغة برمجة معينة، وستؤتي ثمارها طوال مسيرتك المهنية.

تعمق أكثر

  • MDN Web Docs: Regular expressions - الدليل الشامل لـ regex في JavaScript، لكن المفاهيم تنطبق في كل مكان تقريبًا.
  • Wikipedia: Regular expression - غوص عميق في نظرية وتاريخ علوم الكمبيوتر.
  • Regular-Expressions.info - موقع تعليمي ومرجعي شامل ومفصل بشكل لا يصدق.
  • Google RE2 Syntax - نظرة مثيرة للاهتمام على محرك regex شهير يعتمد على DFA ويركز على الأداء.
  • PCRE Man Pages - دليل التعابير النمطية المتوافقة مع Perl (PCRE)، وهي الصيغة التي ألهمت العديد من نكهات regex الحديثة.

انتهينا من النظرية. حان وقت التطبيق — 100% في متصفحك.

جرّب الأداة: مختبر التعابير النمطية