কম্পিউটার

রুবি রেগুলার এক্সপ্রেশনের ভিতরে শর্তসাপেক্ষ ব্যবহার করা

2013 সালে রুবি 2.0 ফেরত পাঠানো অনেকগুলি নতুন বৈশিষ্ট্যের মধ্যে, আমি যেটির দিকে সবচেয়ে কম মনোযোগ দিয়েছিলাম তা হল নতুন রেগুলার এক্সপ্রেশন ইঞ্জিন, Onigmo৷ সর্বোপরি, রেগুলার এক্সপ্রেশন হল রেগুলার এক্সপ্রেশন - রুবি কীভাবে সেগুলি প্রয়োগ করে তা কেন আমি চিন্তা করব?

যেহেতু দেখা যাচ্ছে, Onigmo regex ইঞ্জিনে আপনার রেগুলার এক্সপ্রেশনের অভ্যন্তরে শর্তসাপেক্ষ ব্যবহার করার ক্ষমতা সহ এর আস্তিনে কিছু সুন্দর কৌশল রয়েছে।

এই পোস্টে আমরা রেজেক্স কন্ডিশনালগুলিতে ডুব দেব, সেগুলিকে রুবির বাস্তবায়নের কৌশলগুলি সম্পর্কে জানব এবং রুবির সীমাবদ্ধতাগুলিকে ঘিরে কাজ করার জন্য কয়েকটি কৌশল নিয়ে আলোচনা করব৷ চলুন শুরু করা যাক!

গ্রুপ এবং ক্যাপচারিং

রেগুলার এক্সপ্রেশনে কন্ডিশনাল বোঝার জন্য, আপনাকে প্রথমে গ্রুপিং এবং ক্যাপচারিং বুঝতে হবে।

কল্পনা করুন যে আপনার কাছে মার্কিন উদ্ধৃতির একটি তালিকা রয়েছে:

Fayetteville, AR
Seattle, WA

আপনি রাজ্যের সংক্ষিপ্ত নাম থেকে শহরের নাম আলাদা করতে চান। এটি করার একটি উপায় হল একাধিক ম্যাচ সম্পাদন করা:

PLACE = "Fayetteville, AR"

# City: Match any char that's not a comma
PLACE.match(/[^,]+/) 
# => #<MatchData "Fayetteville">

# Separator: Match a comma and optional spaces
PLACE.match(/, */)
# => #<MatchData ", ">

# State: Match a 2-letter code at the end of the string. 
PLACE.match(/[A-Z]{2}$/) 
# => #<MatchData "AR">

এই কাজ করে, কিন্তু এটা খুব শব্দপূর্ণ. গোষ্ঠীগুলি ব্যবহার করে, আপনি শুধুমাত্র একটি নিয়মিত অভিব্যক্তি দিয়ে শহর এবং রাজ্য উভয়ই ক্যাপচার করতে পারেন।

তো চলুন উপরের রেগুলার এক্সপ্রেশনগুলিকে একত্রিত করি এবং প্রতিটি বিভাগকে বন্ধনী দিয়ে ঘিরে রাখি। প্যারেন্স হল আপনি যেভাবে জিনিসগুলিকে রেগুলার এক্সপ্রেশনে গ্রুপ করেন।

PLACE = "Fayetteville, AR"
m = PLACE.match(/([^,]+)(, *)([A-Z]{2})/) 
# => #<MatchData "Fayetteville, AR" 1:"Fayetteville" 2:", " 3:"AR">

আপনি দেখতে পাচ্ছেন, উপরের অভিব্যক্তিটি শহর এবং রাজ্য উভয়কেই ক্যাপচার করে। আপনি MatchData ব্যবহার করে সেগুলি অ্যাক্সেস করুন একটি অ্যারের মত:

m[1]
# => "Fayetteville"
m[3]
# => "AR"

গ্রুপিংয়ের সমস্যা, যেমনটি উপরে করা হয়েছে, ক্যাপচার করা ডেটা একটি অ্যারেতে রাখা হয়। অ্যারেতে এর অবস্থান পরিবর্তন হলে, আপনাকে আপনার কোড আপডেট করতে হবে বা আপনি এইমাত্র একটি বাগ প্রবর্তন করেছেন।

উদাহরণস্বরূপ, আমরা সিদ্ধান্ত নিতে পারি যে ", " ক্যাপচার করা বোকামি চরিত্র. তাই আমরা রেগুলার এক্সপ্রেশনের সেই অংশের চারপাশের প্যারেনগুলি সরিয়ে দিই:

m = PLACE.match(/([^,]+), *([A-Z]{2})/) 
# => #<MatchData "Fayetteville, AR" 1:"Fayetteville" 2:"AR">

m[3]
# => nil

কিন্তু এখন m[3] আর রাজ্য ধারণ করে না - বাগ শহর।

নামযুক্ত গ্রুপ

আপনি রেগুলার এক্সপ্রেশন গ্রুপের নামকরণ করে অনেক বেশি শব্দার্থিক করতে পারেন। সিনট্যাক্সটি আমরা ঠিক কি ব্যবহার করেছি তার অনুরূপ। আমরা রেজেক্সকে প্যারেন্সে ঘিরে রাখি, এবং এইভাবে নাম উল্লেখ করি:

/(?<groupname>regex)/

যদি আমরা এটিকে আমাদের শহর/রাষ্ট্রের নিয়মিত অভিব্যক্তিতে প্রয়োগ করি, তাহলে আমরা পাই:

m = PLACE.match(/(?<city>[^,]+), *(?<state>[A-Z]{2})/)
# => #<MatchData "Fayetteville, AR" city:"Fayetteville" state:"AR">

এবং আমরা MatchData ব্যবহার করে ক্যাপচার করা ডেটা অ্যাক্সেস করতে পারি হ্যাশের মত:

m[:city] 
# => "Fayetteville"

শর্তাবলী

রেগুলার এক্সপ্রেশনে শর্তাবলী /(?(A)X|Y)/ রূপ নেয় . এখানে সেগুলি ব্যবহার করার কয়েকটি বৈধ উপায় রয়েছে:

# If A is true, then evaluate the expression X, else evaluate Y
/(?(A)X|Y)/

# If A is true, then X
/(?(A)X)/

# If A is false, then Y
/(?(A)|Y)/

আপনার অবস্থার জন্য সবচেয়ে সাধারণ দুটি বিকল্প, A হল:

  • কোন নাম বা সংখ্যাযুক্ত গোষ্ঠী ক্যাপচার করা হয়েছে?
  • একটি দৃষ্টিভঙ্গি কি সত্য বলে মূল্যায়ন করে?

চলুন দেখে নেই কিভাবে সেগুলো ব্যবহার করবেন:

কোন গোষ্ঠী কি ধরা পড়েছে?

একটি গোষ্ঠীর উপস্থিতি পরীক্ষা করতে, ?(n) ব্যবহার করুন সিনট্যাক্স, যেখানে n একটি পূর্ণসংখ্যা, বা <> দ্বারা বেষ্টিত একটি গোষ্ঠীর নাম অথবা '' .

# Has group number 1 been captured?
/(?(1)foo|bar)/

# Has a group named "mygroup" been captured?
/(?(<mygroup>)foo|bar)/

উদাহরণ

কল্পনা করুন আপনি মার্কিন টেলিফোন নম্বর পার্স করছেন। এই সংখ্যাগুলির একটি তিন-সংখ্যার এলাকা কোড আছে যা ঐচ্ছিক যদি না নম্বরটি একটি দিয়ে শুরু হয়৷

1-800-555-1212 # Valid
800-555-1212 # Valid
555-1212 # Valid

1-555-1212 # INVALID!!

নম্বরটি 1 দিয়ে শুরু হলেই আমরা এলাকা কোডকে একটি প্রয়োজনীয়তা তৈরি করতে শর্তসাপেক্ষ ব্যবহার করতে পারি।

# This regular expression looks complex, but it's made of simple pieces
# `^(1-)?` Does the string start with "1-"? If so, capture it as group 1
# `(?(1)` Was anything captured in group one?
# `\d{3}-` if so, do a required match of three digits and a dash (the area code)
# `|(\d{3}-)?` if not, do an optional match of three digits and a dash (area code)
# `\d{3}-\d{4}` match the rest of the phone number, which is always required.

re = /^(1-)?(?(1)\d{3}-|(\d{3}-)?)\d{3}-\d{4}/

"1-800-555-1212".match(re)
#=> #<MatchData "1-800-555-1212" 1:"1-" 2:nil>

"800-555-1212".match(re)
#=> #<MatchData "800-555-1212" 1:nil 2:"800-">

"555-1212".match(re)
#=> #<MatchData "555-1212" 1:nil 2:nil>

"1-555-1212".match(re)
=> nil

সীমাবদ্ধতা

গ্রুপ-ভিত্তিক কন্ডিশনাল ব্যবহার করার সাথে একটি সমস্যা হল যে একটি গোষ্ঠীর সাথে মিল করা স্ট্রিংয়ের সেই অক্ষরগুলিকে "গ্রাহ্য" করে। সেই অক্ষরগুলি শর্তসাপেক্ষে ব্যবহার করা যাবে না, তারপর।

উদাহরণস্বরূপ, যদি "USD" পাঠ্যটি উপস্থিত থাকে তবে নিম্নলিখিত কোডটি 100 এর সাথে মেলানোর চেষ্টা করে এবং ব্যর্থ হয়:

"100USD".match(/(USD)(?(1)\d+)/) # nil

পার্ল এবং অন্যান্য কিছু ভাষায়, আপনি আপনার শর্তসাপেক্ষে একটি লুক-হেড বিবৃতি যোগ করতে পারেন। এটি আপনাকে স্ট্রিংয়ের যেকোনো জায়গায় পাঠ্যের উপর ভিত্তি করে শর্তসাপেক্ষ ট্রিগার করতে দেয়। কিন্তু রুবির এটা নেই, তাই আমাদের একটু সৃজনশীল হতে হবে।

লুক-এরাউন্ড

সৌভাগ্যবশত, আমরা লুক-অ্যারাউন্ড এক্সপ্রেশনের অপব্যবহার করে রুবির রেজেক্স কন্ডিশনালের সীমাবদ্ধতাগুলিকে ঘিরে কাজ করতে পারি।

একটি চারপাশে চেহারা কি?

সাধারনত, রেগুলার এক্সপ্রেশন পার্সার আপনার স্ট্রিং এর মধ্য দিয়ে শুরু থেকে শেষ পর্যন্ত মিল খুঁজতে থাকে। এটি একটি ওয়ার্ড প্রসেসরে কার্সারকে বাম থেকে ডানে সরানোর মতো।

সামনের দিকে তাকানো এবং পিছনের দিকে তাকানো অভিব্যক্তি একটু ভিন্নভাবে কাজ করে। তারা আপনাকে কোনো অক্ষর ব্যবহার না করে স্ট্রিং পরিদর্শন করতে দেয়। সেগুলি শেষ হয়ে গেলে, কার্সারটি শুরুতে ঠিক একই জায়গায় রেখে দেওয়া হয়।

চারপাশের চেহারার একটি দুর্দান্ত ভূমিকার জন্য, সামনের দিকে তাকাতে এবং পিছনের দিকে তাকাতে দক্ষতা অর্জনের জন্য রেক্সেগের গাইড দেখুন

সিনট্যাক্সটি এরকম দেখাচ্ছে:

-এ 100 মেলে শব্দটি অনুসরণ না করে -এ 7 টির সাথে মিলে যায়৷ -এ 7 টির সাথে মিলে যায়৷
প্রকার সিনট্যাক্স উদাহরণ
আগে তাকান (?=query) \d+(?= dollars) "100 ডলার"
আগে নেতিবাচক তাকান (?!query) \d+(?! dollars) 100 মেলে যদি এটি "ডলার"
পিছনে তাকান (?<=query) (?<=lucky )\d "ভাগ্যবান 7"
নেতিবাচক চেহারা পিছনে (?<!query) (?<!furious )\d "ভাগ্যবান 7"

শর্ত উন্নত করতে চারপাশে অপব্যবহার করা

আমাদের শর্তসাপেক্ষে, আমরা কেবলমাত্র ইতিমধ্যেই সেট করা গোষ্ঠীর অস্তিত্ব সম্পর্কে জিজ্ঞাসা করতে পারি। সাধারণত, এর মানে হল গ্রুপের বিষয়বস্তু গ্রাস করা হয়েছে এবং শর্তসাপেক্ষে উপলভ্য নয়।

কিন্তু আপনি কোনো অক্ষর ব্যবহার না করেই একটি গ্রুপ সেট করতে একটি লুক-এহেড ব্যবহার করতে পারেন! আপনার মন কি এখনও বিস্ফোরিত?

এই কোডটি মনে রাখবেন যা কাজ করেনি?

"100USD".match(/(USD)(?(1)\d+)/) # nil

সামনের দিকে গোষ্ঠীটিকে ক্যাপচার করার জন্য যদি আমরা এটিকে পরিবর্তন করি, এটি হঠাৎ করেই ঠিক কাজ করে:

"100USD".match(/(?=.*(USD))(?(1)\d+)/)
=> #<MatchData "100" 1:"USD">

আসুন সেই ক্যোয়ারীটি ভেঙে ফেলি এবং দেখুন কি হচ্ছে:

  • (?=.*(USD)) সামনের দিকে তাকান ব্যবহার করে, "USD" এর জন্য পাঠ্য স্ক্যান করুন এবং গ্রুপ 1 এ ক্যাপচার করুন
  • (?(1) যদি গ্রুপ 1 বিদ্যমান থাকে
  • \d+ তারপর এক বা একাধিক সংখ্যা মেলে

বেশ ঝরঝরে, হাহ?


  1. জাভাস্ক্রিপ্ট রেগুলার এক্সপ্রেশন

  2. রুবিতে ল্যাম্বডাস ব্যবহার করা

  3. রুবি নিয়মিত এক্সপ্রেশন আয়ত্ত

  4. জাভা রেগুলার এক্সপ্রেশন ব্যবহার করে স্ট্রিং থেকে সংখ্যা বের করুন