المسارات
/
Elixir
Elixir
/
التمارين
/
محلّل التاريخ
محلّل التاريخ

محلّل التاريخ

تمرين تعلّمي

مقدمة

التعابير النمطية

التعابير النمطية (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"

يمكنك أن ترى أن بينها بعض أوجه التشابه، فتقرر كتابة بعض أنماط التعابير النمطية القابلة للتركيب.

1. طابِق اليوم والشهر والسنة من تاريخ

نفّذ 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

2. طابِق يوم الأسبوع وشهر السنة

نفّذ day_names/0 وmonth_names/0 لتُرجع كلٌّ منهما نمطًا نصيًا يطابق، عند ترجمته، اسم يوم الأسبوع واسم شهر السنة على التوالي.

"Tuesday" =~ DateParser.day_names() |> Regex.compile!()
# => true
"June" =~ DateParser.month_names() |> Regex.compile!()
# => true

3. التقط اليوم والشهر والسنة

نفّذ 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"}

4. ادمج عمليات الالتقاط لالتقاط التاريخ بأكمله

نفّذ 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"}

5. ضيّق الالتقاط بحيث يطابق التاريخ فقط

نفّذ 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"
#   }
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
Elixir Exercism

مستعد لبدء محلّل التاريخ؟

سجّل في Exercism لتتعلّم وتتقن Elixir عبر 58 مفهومًا168 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.