التعابير النمطية (regex) أداة قوية للتعامل مع السلاسل النصية في Elixir. تتبع التعابير النمطية في Elixir مواصفة PCRE (Perl Compatible Regular Expressions). تُجمَّع أنماط السلاسل النصية التي تمثّل معنى التعبير النمطي أولًا، ثم تُستخدم لمطابقة سلسلة نصية كاملة أو جزء منها.
في Elixir، الطريقة الأكثر شيوعًا لإنشاء التعابير النمطية هي استخدام sigil بالصيغة ~r. توفّر أدوات sigil اختصارات من نوع السكر النحوي للمهام الشائعة في Elixir. ولمطابقة سلسلة نصية حرفية، يمكننا استخدام السلسلة نفسها كنمط يتبع sigil.
~r/test/
يفيد العامل =~/2 في إجراء مطابقة تعبير نمطي على سلسلة نصية لإرجاع نتيجة من نوع boolean.
"this is a test" =~ ~r/test/
# => true
ملاحظتان حول استخدام sigil:
/
\)تعريف نطاق من المحارف باستخدام الأقواس المربعة [] يُنشئ صنف محارف. سيطابق هذا أي محرف واحد مع أيٍّ من محارف الصنف. يمكنك أيضًا تحديد نطاق من المحارف مثل a-z، بشرط أن تمثّل البداية والنهاية نطاقًا متصلًا من نقاط الترميز.
regex = ~r/[a-z][ADKZ][0-9][!?]/
"jZ5!" =~ regex
# => true
"jB5?" =~ regex
# => false
تجعل أصناف المحارف المختصرة النمط أكثر إيجازًا. على سبيل المثال:
\d اختصار لـ [0-9] (أي رقم)\w اختصار لـ [A-Za-z0-9_] (أي محرف "كلمة")\s اختصار لـ [ \t\r\n\f] (أي محرف مسافة بيضاء)وعند استخدام صنف محارف مختصر خارج sigil، يجب تهريبه: "\\d"
تستخدم التناوبات الرمز | كمحرف خاص للدلالة على مطابقة أحد الأمرين أو الآخر
regex = ~r/cat|bat/
"bat" =~ regex
# => true
"cat" =~ regex
# => true
تسمح المكمّمات بنمط متكرر في التعبير النمطي. وهي تؤثر في المجموعة التي تسبق المكمّم.
{N, M} حيث N هو الحد الأدنى لعدد التكرارات، وM هو الحد الأقصى{N,} مطابقة N تكرار أو أكثر
{0,} على هيئة *: مطابقة صفر تكرار أو أكثر{1,} على هيئة +: مطابقة تكرار واحد أو أكثر{,N} مطابقة حتى N من التكراراتتُستخدم الأقواس الهلالية () للدلالة على المجموعات و_الالتقاطات_. وقد تُلتقط المجموعة أيضًا في بعض الحالات لتُرجَع للاستخدام. وفي Elixir، قد تكون هذه مسمّاة أو غير مسمّاة. وتُسمّى الالتقاطات بإضافة ?<name> بعد القوس الافتتاحي. وتعمل المجموعات كوحدة واحدة، كما يحدث حين تتبعها المكمّمات.
regex = ~r/(h)at/
Regex.replace(regex, "hat", "\\1op")
# => "hop"
regex = ~r/(?<letter_b>b)/
Regex.scan(regex, "blueberry", capture: :all_names)
# => [["b"], ["b"]]
تُستخدم المراسي لربط التعبير النمطي ببداية السلسلة النصية المطلوب مطابقتها أو نهايتها:
^ يثبّت عند بداية السلسلة النصية$ يثبّت عند نهاية السلسلة النصيةولأن ~r اختصار لـ "pattern" |> Regex.escape() |> Regex.compile!()، يمكنك أيضًا استخدام تداخل النصوص لبناء نمط تعبير نمطي ديناميكيًا:
anchor = "$"
regex = ~r/end of the line#{anchor}/
"end of the line?" =~ regex
# => false
"end of the line" =~ regex
# => true
أُوكلت إليك مهمة كتابة خدمة تستقبل الأحداث. لكل حدث تاريخ مرتبط به، لكنك تلاحظ أن 3 صيغ مختلفة تُرسَل إلى نقطة نهاية خدمتك:
"01/01/1970""January 1, 1970""Thursday, January 1, 1970"يمكنك أن ترى أن بينها بعض أوجه التشابه، فتقرر كتابة بعض أنماط التعابير النمطية القابلة للتركيب.
نفّذ day/0 وmonth/0 وyear/0 لتُرجع نمطًا نصيًا يطابق، عند ترجمته، المكوّنات الرقمية في "01/01/1970" (dd/mm/yyyy). قد يظهر اليوم والشهر بالصيغة 1 أو 01 (مع حشوهما بالأصفار من اليسار).
"31" =~ DateParser.day() |> Regex.compile!()
# => true
"12" =~ DateParser.month() |> Regex.compile!()
# => true
"1970" =~ DateParser.year() |> Regex.compile!()
# => true
نفّذ day_names/0 وmonth_names/0 لتُرجع كلٌّ منهما نمطًا نصيًا يطابق، عند ترجمته، اسم يوم الأسبوع واسم شهر السنة على التوالي.
"Tuesday" =~ DateParser.day_names() |> Regex.compile!()
# => true
"June" =~ DateParser.month_names() |> Regex.compile!()
# => true
نفّذ capture_day/0 وcapture_month/0 وcapture_year/0 وcapture_day_name/0 وcapture_month_name/0 لتُرجع نمطًا نصيًا يلتقط المكوّنات المناظرة بالأسماء: "day" و"month" و"year" و"day_name" و"month_name".
DateParser.capture_month_name()
|> Regex.compile!()
|> Regex.named_captures("December")
# => %{"month_name" => "December"}
نفّذ capture_numeric_date/0 وcapture_month_name_date() وcapture_day_month_name_date/0 لتُرجع نمطًا نصيًا يلتقط المكوّنات من الجزء 3 باستخدام صيغة التاريخ المناظرة:
"01/01/1970"
"January 1, 1970"
"Thursday, January 1, 1970"
DateParser.capture_numeric_date()
|> Regex.compile!()
|> Regex.named_captures("01/01/1970")
# => %{"day" => "01", "month" => "01", "year" => "1970"}
نفّذ match_numeric_date/0 وmatch_month_name_date/0 وmatch_day_month_name_date/0 لتُرجع تعبيرًا نمطيًا مُترجَمًا يطابق التاريخ فقط، ويمكنه أيضًا التقاط المكوّنات.
"Thursday, January 1, 1970 was the Unix epoch." =~ DateParser.match_day_month_name_date()
# => false
"Thursday, January 1, 1970" =~ DateParser.match_day_month_name_date()
# => true
DateParser.match_day_month_name_date()
|> Regex.named_captures("Thursday, January 1, 1970")
# => %{
# "day" => "1",
# "day_name" => "Thursday",
# "month_name" => "January",
# "year" => "1970"
# }
سجّل في Exercism لتتعلّم وتتقن Elixir عبر 58 مفهومًا168 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.