আপনি ইতিমধ্যেই মূল বিষয়গুলো জানেন: \d আর [A-Z]-এর মতো ক্যারেক্টার ক্লাস, {4} আর +-এর মতো কোয়ান্টিফায়ার, এবং matches?, all-matching-subseqs ও re-replace-এর মতো ওয়ার্ডগুলো। এই অনুশীলনীতে রেগুলার এক্সপ্রেশনের যে অংশগুলো গঠন ও কনটেক্সট বর্ণনা করে, সেগুলো যোগ করা হয়েছে। এগুলো একই regexp ভোকাবুলারিতেই থাকে, লেখা হয় একই R/ / লিটারাল দিয়ে।
অ্যাঙ্কর একটি ক্যারেক্টার নয়, বরং একটি পজিশন-এর সাথে মেলে:
^ ! the start of the string
$ ! the end of the string
\b ! a word boundary
যেহেতু $ মানে স্ট্রিংয়ের শেষ, তাই আক্ষরিক ডলার চিহ্ন বোঝাতে চাইলে \$ লিখুন।
একটি ওয়ার্ড বাউন্ডারি (\b) হলো সেই জায়গা, যেখানে একটি ওয়ার্ড ক্যারেক্টার কোনো নন-ওয়ার্ড ক্যারেক্টারের সাথে মিলিত হয়। ওয়ার্ড ক্যারেক্টার হলো অক্ষর, ডিজিট আর আন্ডারস্কোর; বাকি সবকিছু, অর্থাৎ স্পেস, বিরামচিহ্ন এবং স্ট্রিংয়ের দুই প্রান্ত, বাউন্ডারি হিসেবে গণ্য হয়। তাই \bcat\b শুধু তখনই cat-এর সাথে মেলে, যখন শব্দটি আলাদা হয়ে দাঁড়ায়:
USING: regexp ;
"cat nap" R/ \bcat\b/ re-contains? . ! => t
"scatter" R/ \bcat\b/ re-contains? . ! => f (cat sits inside a word)
গোল বন্ধনী ( ) প্যাটার্নের একটি অংশকে গ্রুপ করে, যাতে একটি কোয়ান্টিফায়ার, বা একটি | ("এটা অথবা ওটা"), পুরো গ্রুপের উপর প্রযোজ্য হয়:
USING: regexp ;
"abab" R/ (ab)+/ matches? . ! => t (+ applies to "ab")
"dog" R/ cat|dog/ matches? . ! => t (either word)
"$100.50" R/ \$\d+(\.\d{2})?/ matches? . ! => t
"$100" R/ \$\d+(\.\d{2})?/ matches? . ! => t (the group is optional)
শেষ প্যাটার্নটির অর্থ: "একটি ডলার চিহ্ন, এক বা তার বেশি ডিজিট, তারপর একটি ডট আর দুটি ডিজিটের একটি ঐচ্ছিক গ্রুপ"।
নোট: Factor-এ একটি গ্রুপ প্যাটার্নটিকে কেবল সাজায়। কিছু রেগুলার এক্সপ্রেশন সরঞ্জামের বিপরীতে, এখানে আপনি গ্রুপ যে টেক্সট ম্যাচ করেছে তা ফিরিয়ে বের করতে পারেন না: কোনো ক্যাপচার-গ্রুপ এক্সট্র্যাকশন নেই, আর
\1-এর মতো ব্যাক-রেফারেন্সও সমর্থিত নয়। কোনো ল্যান্ডমার্কের চারপাশের টেক্সট বাছাই করতে চাইলে বরং লুকঅ্যারাউন্ড ব্যবহার করুন (নিচে)।
একটি লুকঅ্যারাউন্ড বর্তমান জায়গার ঠিক আগে বা ঠিক পরে কী আছে তা যাচাই করে, কিন্তু সেই ক্যারেক্টারগুলোকে ম্যাচের অংশ বানায় না। এটি "জিরো-উইডথ": এটি যা পরীক্ষা করে, তা ফলাফলে অন্তর্ভুক্ত হয় না।
(?=...) ! lookahead: ... must follow
(?!...) ! negative lookahead: ... must NOT follow
(?<=...) ! lookbehind: ... must come before
(?<!...) ! negative lookbehind: ... must NOT come before
লুকঅ্যারাউন্ড সিলেকটিভ: একই ক্যারেক্টার পাশে কী আছে তার উপর নির্ভর করে মেলে কি মেলে না। সাধারণ \d+ প্রতিটি সংখ্যা তুলে নেয়; লুকবিহাইন্ড ও লুকঅ্যাহেড শুধু পাশে সঠিক প্রতিবেশী থাকা সংখ্যাগুলোই রেখে দেয়:
USING: regexp ;
"3 for $10 or 10 for $30" R/ \d+/ all-matching-subseqs .
! => { "3" "10" "10" "30" } (every number)
"3 for $10 or 10 for $30" R/ (?<=\$)\d+/ all-matching-subseqs .
! => { "10" "30" } (only the prices — the bare 3 and 10 drop)
"buy 2 at 15% off, 4 at 30% off" R/ \d+(?=%)/ all-matching-subseqs .
! => { "15" "30" } (only the discounts — the bare 2 and 4 drop)
শেষ /-এর পরে একটি অক্ষর যোগ করলেই পুরো প্যাটার্নের আচরণ বদলে যায়। i এটিকে কেস-ইনসেনসিটিভ করে দেয়:
USING: regexp ;
"REFUND" R/ refund/i matches? . ! => t
"Refund" R/ refund/i matches? . ! => t
all-matching-subseqs আপনাকে ম্যাচ হওয়া টেক্সট দিয়ে দেয়। map-matches আরেক ধাপ এগিয়ে যায়: এটি প্রতিটি ম্যাচের উপর একটি কোয়োটেশন চালায় আর ফলাফলগুলো সংগ্রহ করে। কোয়োটেশনটি ম্যাচের start ইনডেক্স, তার end ইনডেক্স এবং পুরো স্ট্রিং পায়; subseq এই তিনটিকে ম্যাচ হওয়া টেক্সটে পরিণত করে, যেটিকে আপনি এরপর রূপান্তর করেন:
map-matches ( string regexp quot: ( start end string -- obj ) -- seq )
USING: math regexp sequences ;
"a1 b22 c333" R/ \d+/ [ subseq length ] map-matches .
! => { 1 2 3 } (the length of each run of digits)
এর শুধু সাইড-এফেক্টে সীমাবদ্ধ সঙ্গী each-match প্রতিটি ম্যাচে একটি কোয়োটেশন চালায়, কিন্তু কিছুই সংগ্রহ করে না।
আপনি একটি ছোট কোম্পানির হিসাব রাখেন, আর প্রতিদিনের লেনদেন আপনার কাছে পৌঁছায় এলোমেলো লেখার লাইন হিসেবে। পরিমাণ যাচাই করতে, যে চিহ্ন দিয়ে সেগুলো চেনা যায় সেই চিহ্ন ধরে সংখ্যাগুলো বের করতে এবং আরেকবার ভালো করে দেখা দরকার এমন লাইন চিহ্নিত করতে আপনি রেগুলার এক্সপ্রেশন ব্যবহার করবেন।
সঠিক গঠনের একটি পরিমাণ হলো একটি $, তারপর এক বা একাধিক অঙ্ক, আর তারপর ঐচ্ছিক একটি সেন্টের অংশ, যা একটি ডট ও ঠিক দুটি অঙ্ক দিয়ে গঠিত: $100 বা $100.50, কিন্তু $100.5 নয়। valid-amount? ডিফাইন করুন, যা t রিটার্ন করে যখন পুরো স্ট্রিংটি সঠিক গঠনের পরিমাণ হয়।
"$100.50" valid-amount? . ! => t
"$100" valid-amount? . ! => t
"$100.5" valid-amount? . ! => f
dollar-amounts ডিফাইন করুন, যা লাইনে সরাসরি $ চিহ্নের পরে আসা প্রতিটি সংখ্যা ক্রমানুসারে রিটার্ন করে, শুধু অঙ্কগুলো, $ ছাড়া।
"spent $100 and $25 today" dollar-amounts .
! => { "100" "25" }
percentages ডিফাইন করুন, যা এমন প্রতিটি সংখ্যা রিটার্ন করে যার ঠিক পরেই একটি % থাকে।
"up 5% then down 12%" percentages .
! => { "5" "12" }
flagged? ডিফাইন করুন, যা t রিটার্ন করে যখন লাইনটিতে refund বা chargeback বড় হাতের ও ছোট হাতের অক্ষরের যেকোনো মিশ্রণে থাকে।
"Issued a REFUND today" flagged? . ! => t
"a normal sale" flagged? . ! => f
Exercism-এ সাইন আপ করুন, Factor ট্র্যাকের 47টি কনসেপ্ট163টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।