রেগুলার এক্সপ্রেশন (রেজেক্স) হলো অক্ষরের এমন একটি ক্রম, যা টেক্সটে খোঁজার একটি প্যাটার্ন ঠিক করে দেয়।
রেগুলার এক্সপ্রেশনের সিনট্যাক্স শেখানো এই টপিকের পরিধির বাইরে।
আমরা রেজেক্স কাজে লাগাতে jq যে এক্সপ্রেশনগুলো দেয়, সেগুলোতেই মনোযোগ দেব।
ভিন্ন ভিন্ন টুল রেগুলার এক্সপ্রেশনের ভিন্ন ভিন্ন ভার্সন বাস্তবায়ন করে।
jq Oniguruma রেজেক্স লাইব্রেরিটি ব্যবহার করে, যা Perl v5.8-এর রেজেক্সের সাথে বেশিরভাগ ক্ষেত্রেই সামঞ্জস্যপূর্ণ।
jq যে নির্দিষ্ট সিনট্যাক্স ব্যবহার করে, তা Oniguruma GitHub রিপোতে পাওয়া যাবে।
jq-এর রেগুলার এক্সপ্রেশনের জন্য কোনো আলাদা সিনট্যাক্স নেই।
এগুলোকে সহজভাবে স্ট্রিং হিসেবেই লেখা হয়।
মানে হলো, রেগুলার এক্সপ্রেশনের যেকোনো ব্যাকস্ল্যাশ স্ট্রিংয়ের ভেতরে এস্কেপ করতে হবে।
যেমন, ডিজিট ক্যারেক্টার ক্লাসটি (\d) লিখতে হবে "\\d" হিসেবে।
jq-তে রেগুলার এক্সপ্রেশন কয়েকটি ফিল্টারের মধ্যে সীমাবদ্ধ।
কোনো স্ট্রিং একটি প্যাটার্নের সাথে মেলে কি না জানতে হলে test ফিল্টারটি ব্যবহার করুন।
STRING | test(REGEX)
STRING | test(REGEX; FLAGS)
STRING | test([REGEX, FLAGS])
এই ফিল্টারটি একটি বুলিয়ান ফলাফল আউটপুট দেয়।
"Hello World!" | test("W") # => true
"Goodbye Mars" | test("W") # => false
প্যাটার্নের সাথে আসলে যে সাবস্ট্রিংটি মিলেছে তা বের করতে হলে match ফিল্টারটি ব্যবহার করুন।
STRING | match(REGEX)
STRING | match(REGEX; FLAGS)
STRING | match([REGEX, FLAGS])
এই ফিল্টারটি আউটপুট দেয়:
এই উদাহরণটি ব্যাকরেফ সিনট্যাক্স \1 ব্যবহার করে পরপর থাকা দুটি একই স্বরবর্ণ খুঁজে দেখে।
"Hello World!" | match("([aeiou])\\1")
# => empty
"Goodbye Mars" | match("([aeiou])\\1")
# => {
# "offset": 1,
# "length": 2,
# "string": "oo",
# "captures": [
# {
# "offset": 1,
# "length": 1,
# "string": "o",
# "name": null
# }
# ]
# }
match ফিল্টারটি প্রতিটি ম্যাচের জন্য একটি অবজেক্ট রিটার্ন করে।
এই উদাহরণে সব স্বরবর্ণ খুঁজে বের করতে "g" ফ্ল্যাগটি কাজ করতে দেখা যায়।
"Goodbye Mars" | match("[aeiou]"; "g")
# => { "offset": 1, "length": 1, "string": "o", "captures": [] }
# { "offset": 2, "length": 1, "string": "o", "captures": [] }
# { "offset": 6, "length": 1, "string": "e", "captures": [] }
# { "offset": 9, "length": 1, "string": "a", "captures": [] }
match ফিল্টারের মতোই, capture ফিল্টারটি ম্যাচ থাকলে একটি অবজেক্ট রিটার্ন করে।
STRING | capture(REGEX)
STRING | capture(REGEX; FLAGS)
STRING | capture([REGEX, FLAGS])
রিটার্ন করা অবজেক্টটি নেমড ক্যাপচারগুলোর একটি ম্যাপিং।
"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# => {
# "project": "JIRAISSUE",
# "issue_num": "1234"
# }
scan ফিল্টারটি "g" ফ্ল্যাগ-সহ match-এর মতোই।
STRING | scan(REGEX)
STRING | scan(REGEX; FLAGS)
scan সাবস্ট্রিংয়ের একটি স্ট্রিম আউটপুট দেবে।
"Goodbye Mars" | scan("[aeiou]")
# => "o"
# "o"
# "e"
# "a"
সাবস্ট্রিংগুলো ক্যাপচার করতে [...] অ্যারে কনস্ট্রাক্টরটি ব্যবহার করুন।
"Goodbye Mars" | [ scan("[aeiou]") ]
# => ["o", "o", "e", "a"]
স্ট্রিংয়ের যে অংশগুলো আপনি রাখতে চান সেগুলো যদি জানা থাকে, তবে match বা scan ব্যবহার করুন।
আর যে অংশগুলো আপনি বাদ দিতে চান সেগুলো যদি জানা থাকে, তবে split ব্যবহার করুন।
STRING | split(REGEX; FLAGS)
একটি মাত্র আর্গুমেন্ট নেওয়া split ফিল্টারটি তার আর্গুমেন্টকে একটি ফিক্সড স্ট্রিং হিসেবে ধরে।
split-এর সাথে রেজেক্স ব্যবহার করতে হলে আপনাকে অবশ্যই দ্বিতীয় আর্গুমেন্টটি দিতে হবে; খালি স্ট্রিং দিলেও চলবে।
যেকোনো হোয়াইটস্পেসে একটি স্ট্রিং ভাগ করার একটি উদাহরণ।
"first second third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
sub ও gsub ফিল্টার ইনপুট স্ট্রিংটি রূপান্তর করতে পারে, ইনপুটের যে অংশগুলো মিলেছে সেগুলোকে একটি রিপ্লেসমেন্ট স্ট্রিং দিয়ে বদলে দিয়ে।
শুধু প্রথম ম্যাচটি বদলাতে sub ব্যবহার করুন।
সব ম্যাচ বদলাতে gsub ব্যবহার করুন।
STRING | sub(REGEX; REPLACEMENT)
STRING | sub(REGEX; REPLACEMENT; FLAGS)
STRING | gsub(REGEX; REPLACEMENT)
STRING | gsub(REGEX; REPLACEMENT; FLAGS)
"Goodnight kittens. Goodnight mittens." | sub("night"; " morning")
# => "Good morning kittens. Goodnight mittens."
"Goodnight kittens. Goodnight mittens." | gsub("night"; " morning")
# => "Good morning kittens. Good morning mittens."
রিপ্লেসমেন্ট টেক্সট মিলে যাওয়া সাবস্ট্রিংগুলোকে নির্দেশ করতে পারে; এজন্য নেমড ক্যাপচার ও স্ট্রিং ইন্টারপোলেশন ব্যবহার করুন।
"Some 3-letter acronyms: gnu, csv, png"
| gsub( "\\b(?<tla>[[:alpha:]]{3})\\b"; # find words 3 letters long
"\(.tla | ascii_upcase)" ) # upper-case the match
# => "Some 3-letter acronyms: GNU, CSV, PNG"
উপরের সব ফিল্টারে FLAGS হলো সাপোর্টেড ফ্ল্যাগগুলোর শূন্য বা ততোধিক নিয়ে গঠিত একটি স্ট্রিং।
g - গ্লোবাল সার্চ (শুধু প্রথমটি নয়, সব ম্যাচ খুঁজে বের করে)i - কেস-ইনসেনসিটিভ সার্চm - মাল্টি লাইন মোড ('.' নতুন লাইনেও ম্যাচ করবে)n - খালি ম্যাচ উপেক্ষা করেp - s ও m দুটি মোডই চালু থাকেs - সিঙ্গল লাইন মোড ('^' -> '\A', '$' -> '\Z')l - সবচেয়ে লম্বা সম্ভাব্য ম্যাচ খুঁজে বের করেx - এক্সটেন্ডেড রেজেক্স ফরম্যাট (হোয়াইটস্পেস ও কমেন্ট উপেক্ষা করে)যেমন
"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# or with Extended formatting
"JIRAISSUE-1234" | capture("
(?<project> \\w+ ) # the Jira project
- # followed by a hyphen
(?<issue_num> \\d+ ) # followed by digits
"; "x")
আপনি একটি কোম্পানিতে রেগুলার এক্সপ্রেশন স্পেশালিস্ট হিসেবে নিয়োগ পেয়েছেন, যে কোম্পানি একটি চ্যাটবট তৈরি করছে।
এটি এখন উন্নয়নের একেবারে প্রাথমিক পর্যায়ে আছে। আপনার মিশন হলো রেগুলার এক্সপ্রেশন ব্যবহার করে প্রাকৃতিক ভাষা বোঝা ও তৈরি করার চ্যাটবটের ক্ষমতা বাড়ানো।
বুদ্ধিমান হওয়ার পাশাপাশি চ্যাটবট একজন বিশ্বস্ত সহকারীও। চ্যাটবটকে কিছু জিজ্ঞাসা করতে হলে ব্যবহারকারীকে কমান্ডের একেবারে প্রথমে "Chatbot" শব্দটি বলতে হবে। কিওয়ার্ডটি বড় হাতের অক্ষরে লেখা হোক বা ছোট হাতের অক্ষরে, তাতে কিছু যায় আসে না। গুরুত্বপূর্ণ হলো শব্দটির অবস্থান।
is_valid_command ফাংশনটি ইমপ্লিমেন্ট করুন, যা ব্যবহারকারী কখন কমান্ড দিচ্ছে তা চিনতে চ্যাটবটকে সাহায্য করে।
"Chatbot, play a song from the 80's." | is_valid_command
# => true
"Hey Chatbot, where is the closest pharmacy?" | is_valid_command
# => false
"CHATBOT, do you have a solution for this challenge?" | is_valid_command
# => true
মানুষ কীভাবে মনের ভাব প্রকাশ করতে ইমোজি ব্যবহার করে, তা বুঝতে চ্যাটবটের বেশ কষ্ট হয়। চ্যাটবট ব্যবহারকারীর মেসেজ পেলে প্রতিটি ইমোজি লেখা হয় "emoji" স্ট্রিং এবং তার পরে একটি id নম্বর হিসেবে।
remove_emoji মেথডটি ইমপ্লিমেন্ট করুন, যা একটি স্ট্রিং নিয়ে মেসেজের সব ইমোজি সরিয়ে দেয়।
যে লাইনগুলোতে ইমোজি নেই, সেগুলো অপরিবর্তিত অবস্থায় রিটার্ন করা উচিত। শুধু emoji স্ট্রিংটি সরিয়ে দিন। হোয়াইটস্পেস বদলাবেন না।
"I love playing videogames emoji3465 it's one of my hobbies" | remove_emoji
# => "I love playing videogames it's one of my hobbies"
চ্যাটবটের সাথে কথোপকথনের কোনো এক পর্যায়ে ব্যবহারকারী একটি ফোন নম্বর দেবে। চ্যাটবট কেবল একটি নির্দিষ্ট ফরম্যাটের নম্বরেই কল করতে পারে।
check_phone_number ফাংশনটি ইমপ্লিমেন্ট করুন।
নম্বরটি বৈধ হলে চ্যাটবট ব্যবহারকারীকে ধন্যবাদ জানিয়ে এবং নম্বরটি নিশ্চিত করে একটি বার্তা দেয়। নম্বরটি অবৈধ হলে চ্যাটবট ব্যবহারকারীকে জানায় যে ফোন নম্বরটি বৈধ নয়।
প্রত্যাশিত ফরম্যাট হলো (+NN) NNN-NNN-NNN, যেখানে N একটি ডিজিট।
"chatbot my phone number is (+34) 659-771-594" | check_phone_number
# => "Thanks! Your phone number is OK."
"chatbot, call me at 659-771-594" | check_phone_number
# => "Oops, it seems like I can't reach out to 659-771-594."
চ্যাটবট সত্যিই একটি কৌতূহলী সফটওয়্যার। কোনো নির্দিষ্ট বিষয়ে এটি ইন্টারনেটে খুঁজতে পারলেও, প্রাসঙ্গিক তথ্য খুঁজে পেতে ব্যবহারকারীদের কাছে ঘুরে দেখার মতো দারুণ সব ওয়েবসাইট সম্পর্কে জানতে ভালোবাসে।
উদাহরণ কথোপকথন:
Chatbot: হে ব্যবহারকারী, আমি JavaScript-এ কোড করা শিখতে চাই। আপনি কি এমন কোনো দারুণ ওয়েবসাইট চেনেন যেখানে আমি শিখতে পারব?
User: আমি exercism.org থেকে অনেক কিছু শিখেছি, ওখানে দারুণ সব জিনিস আছে।
get_domains ফাংশনটি ইমপ্লিমেন্ট করুন, যা ওয়েবসাইট ডোমেইনগুলোর একটি অ্যারে রিটার্ন করে।
"I learned a lot from exercism.org and google.com" | get_domains
# => ["exercism.org", "google.com"]
ভদ্র একটি চ্যাটবট ব্যবহারকারীদের নাম ধরে সম্বোধন করে। ব্যবহারকারী নিজের পরিচয় দিলে আমাদের চ্যাটবট তার নাম শনাক্ত করে বন্ধুত্বপূর্ণ অভিবাদন জানায়।
nice_to_meet_you ফাংশনটি লিখুন।
ইনপুট স্ট্রিংয়ে "My name is Someone." থাকলে নামটি ক্যাপচার করুন এবং "Nice to meet you, Someone." স্ট্রিংটি রিটার্ন করুন।
"My name is Jean-Luc" | nice_to_meet_you
# => "Nice to meet you, Jean-Luc"
"একটার পর একটা ফিচার যোগ করার নেশা"-র কাছে হার মেনে আমরা চ্যাটবটে একটি CSV পার্সিং ফাংশন যোগ করব।
parse_csv ফাংশনটি ইমপ্লিমেন্ট করুন, যা একটি স্ট্রিং নিয়ে ফলস্বরূপ ফিল্ডগুলোর একটি অ্যারে রিটার্ন করে।
ফিল্ড আলাদা করার নিয়ম হবে "কমা এবং তার পরে ঐচ্ছিক হোয়াইটস্পেস"।
CSV ফরম্যাটের কোনো এজ কেস নিয়ে আমরা মাথা ঘামাব না (যেমন কমা থাকা ফিল্ড)।
"first, second,third, fourth" | parse_csv
# => ["first", "second", "third", "fourth"]
Exercism-এ সাইন আপ করুন, jq ট্র্যাকের 12টি কনসেপ্ট75টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।