ট্র্যাক
/
Elixir
Elixir
/
অনুশীলনী
/
তারিখ পার্সার
তারিখ পার্সার

তারিখ পার্সার

লার্নিং অনুশীলনী

ভূমিকা

রেগুলার এক্সপ্রেশন

রেগুলার এক্সপ্রেশন (regex) হলো এলিক্সিরে স্ট্রিং নিয়ে কাজ করার একটি শক্তিশালী হাতিয়ার। এলিক্সিরে রেগুলার এক্সপ্রেশন PCRE স্পেসিফিকেশন (Perl Compatible Regular Expressions, অর্থাৎ পার্ল-সামঞ্জস্যপূর্ণ রেগুলার এক্সপ্রেশন) অনুসরণ করে। রেগুলার এক্সপ্রেশনের অর্থ প্রকাশ করে এমন স্ট্রিং প্যাটার্ন প্রথমে কম্পাইল করা হয়, তারপর স্ট্রিংয়ের পুরোটা বা আংশিক অংশ মেলাতে ব্যবহার করা হয়।

এলিক্সিরে রেগুলার এক্সপ্রেশন তৈরির সবচেয়ে কমন উপায় হলো ~r সিগিল ব্যবহার করা। সিগিল এলিক্সিরের কমন কাজগুলোর জন্য সিনট্যাক্টিক সুগার শর্টকাট দেয়। একটি স্ট্রিং লিটারাল মেলাতে সিগিলের পরে ওই স্ট্রিংটিকেই প্যাটার্ন হিসেবে ব্যবহার করা যায়।

~r/test/

=~/2 অপারেটরটি একটি স্ট্রিংয়ে রেগুলার এক্সপ্রেশন ম্যাচ চালিয়ে boolean ফলাফল রিটার্ন করতে কাজে লাগে।

"this is a test" =~ ~r/test/
# => true

সিগিল ব্যবহারের ব্যাপারে দুটি বিষয় মনে রাখবেন:

  • আপনার প্রয়োজন অনুযায়ী / ছাড়াও অনেক ভিন্ন ডিলিমিটার ব্যবহার করা যায়
  • স্ট্রিং প্যাটার্ন আগেই এস্কেপড থাকে; রেগুলার এক্সপ্রেশন ব্যবহার না করে প্যাটার্নটি স্ট্রিং হিসেবে লিখলে ব্যাকস্ল্যাশ (\) এস্কেপ করতে হবে

ক্যারেক্টার ক্লাস

স্কয়ার ব্র্যাকেট [] দিয়ে ক্যারেক্টারের একটি রেঞ্জ মেলালে একটি ক্যারেক্টার ক্লাস সংজ্ঞায়িত হয়। এটি ক্লাসের ভেতরের যেকোনো একটি ক্যারেক্টারের সাথে মিলবে। আপনি 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]-এর সংক্ষিপ্ত রূপ (যেকোনো হোয়াইটস্পেস ক্যারেক্টার)

সিগিলের বাইরে শর্টহ্যান্ড ক্যারেক্টার ক্লাস ব্যবহার করলে সেটি এস্কেপ করতে হবে: "\\d"

অল্টারনেশন

অল্টারনেশন একটি বিশেষ ক্যারেক্টার | ব্যবহার করে বোঝায় যে একটি অথবা আরেকটির সাথে মিলবে

regex = ~r/cat|bat/
"bat" =~ regex
# => true
"cat" =~ regex
# => true

কোয়ান্টিফায়ার

কোয়ান্টিফায়ার রেগুলার এক্সপ্রেশনে একটি পুনরাবৃত্ত প্যাটার্নের সুযোগ দেয়। এগুলো কোয়ান্টিফায়ারের আগে থাকা গ্রুপের ওপর প্রভাব ফেলে।

  • {N, M} যেখানে N হলো পুনরাবৃত্তির সর্বনিম্ন সংখ্যা আর M হলো সর্বোচ্চ
  • {N,} N বা তার চেয়ে বেশি পুনরাবৃত্তি মেলায়
    • {0,}-কে * হিসেবেও লেখা যায়: শূন্য বা তার বেশি পুনরাবৃত্তি মেলায়
    • {1,}-কে + হিসেবেও লেখা যায়: এক বা তার বেশি পুনরাবৃত্তি মেলায়
  • {,N} সর্বোচ্চ Nটি পুনরাবৃত্তি পর্যন্ত মেলায়

গ্রুপ

গোল বন্ধনী () গ্রুপ ও ক্যাপচার বোঝাতে ব্যবহৃত হয়। কিছু ক্ষেত্রে গ্রুপটি ব্যবহারের জন্য ফেরত দিতে _ক্যাপচার_ও করা যায়। এলিক্সিরে এগুলো নামযুক্ত বা নামহীন হতে পারে। খোলা বন্ধনীর পরে ?<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

নির্দেশনা

আপনাকে এমন একটি সার্ভিস লেখার দায়িত্ব দেওয়া হয়েছে যা ইভেন্ট গ্রহণ করে। প্রতিটি ইভেন্টের সাথে একটি তারিখ যুক্ত থাকে, কিন্তু আপনি লক্ষ্য করলেন যে আপনার সার্ভিসের এন্ডপয়েন্টে ৩টি ভিন্ন ফরম্যাটে ডেটা জমা হচ্ছে:

  • "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 ইমপ্লিমেন্ট করুন যাতে একটি স্ট্রিং প্যাটার্ন রিটার্ন হয়, যেটি ৩ নম্বর অংশের উপাদানগুলো সংশ্লিষ্ট ডেট ফরম্যাট ব্যবহার করে ক্যাপচার করে:

  • সংখ্যাগত তারিখ - "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টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।