কাজের জায়গায় ভুল থেকে শেখা: regex 'র একটা খুব কমন বিষয় যেটা এতদিন ভুল জানতাম

কাজের জায়গায় ভুল থেকে শেখা: regex 'র একটা খুব কমন বিষয় যেটা এতদিন ভুল জানতাম 
৩ ফেব্রুয়ারি, শনিবার, ২০২৪

রেগুলার এক্সপ্রেশন (Regular Expression or regex) নিয়ে আগেও কিছু কাজ করেছি। টেক্সট ভিত্তিক কিছু প্রবলেম সল্ভ করতে regex খুবই শক্তিশালী একটা টুল। regex -এ ".*" নোটেশন যে any number of character(s) বোঝায় - এটা মোটামুটি সবাই জানে। আমিও জানতাম -- কিন্তু আসলে কথাটা পুরাপুরি সত্যি না, ভুল জানতাম। ভুলটা কী সেইটাই আমার কোডে একটা বাগ ফিক্স করতে গিয়ে শিখলাম,  সঠিক উপায় সম্পর্কে জানলাম। আর তাই আজকের ব্লগ লিখছি। 

[সাইড নোট: tl;dr: মানে যে, too long; didn't read: সেটাও খুব বেশিদিন না, এই কিছুদিন আগে জানলাম। লম্বা পোস্টের মূল বক্তব্য ২-১ লাইনে সারমর্ম আকারে লিখতে ব্যবহার হয়! আমার এই লেখার tl;dr হচ্ছে: regex -এ .* মানে 'any number of single character(s)' - কিন্তু newline (\n) এর আওতায় পড়ে না!] 

কাজের জায়গায় খুব সাধারণ একটা কাজ করতে সেদিন regex ব্যবহার করেছি। প্রবলেমটা ছিল এই রকম: একটা টেম্পলেট  XML ডকুমেন্টে <date> ট্যাগের ভেতর আজকের ডেট বসাতে হবে। তো, আমি ভাবলাম, যেহেতু XML ডকুমেন্টটা string আকারে থাকবে, কাজেই regex'র সাবস্টিটিউট মেথড কল করে <date> .. </date> ট্যাগের ভেতরের যাইই থাকুক, সেটা  .* দিয়ে ক্যাপচার করে আজকের ডেট দিয়ে রিপ্লেস করে দিবো। খুবই সহজ: যেমন নিচের কোড:

ছবি ১: regex দিয়ে আজকের ডেট রিপ্লেস করলাম

কোডটা ব্যাখ্যা করি। লাইন ৪-এ পাইথনে datetime মডিউল দিয়ে now() মেথড কল করে এই মুহূর্তের UTC টাইম নিলাম। স্ট্রিং ফরমেটে  "সাল-মাস-তারিখ ঘন্টা:মিনিট:সেকেন্ড" নেয়ার জন্য strftime() মেথড কল করলাম। (%Y দিয়ে চার ডিজিটের সাল নিলাম) । লাইন ৬ - ৮ -এ XML টেম্পলেট স্ট্রিং ডকুমেন্ট বোঝাতে মাল্টিলাইন স্ট্রিং নিলাম, ধরা যাক, <date> -এ ২০২০ আছে। এরপর ইনপুট স্ট্রিং প্রিন্ট করে, লাইন-১১ তে পাইথনের regex মডিউল (re)'র সাবস্টিটিউট মেথড (sub()) কল করলাম, প্রথম প্যারামিটারে টার্গেট প্যাটার্ন অর্থাৎ "<date>[.* যাই থাকুক]</date>" দিলাম, আর সেটা রিপ্লেস করলাম আজকের তারিখ দিয়ে। লাইন-১২ তে আপডেটেড স্ট্রিং টা প্রিন্ট করিয়ে দেখলাম যে কাজ হয়েছে। আজকের তারিখ দিয়ে ডেট রিপ্লেস হয়েছে। হয়ে গেলো!

সমস্যাটা হলো, কেউ একজন ইনপুট টেমপ্লেটটাতে ভুল করে ডেট আর টাইমের মাঝখানে একটা নিউলাইন ঢুকিয়ে দিয়েছিলো, দুই লাইন করে দিয়েছিল। ব্যস, তাতেই আমার কোড আর কাজ করে না। টেম্পলেট ডেট আজকের ডেট দিয়ে রিপ্লেস না হয়ে আগেরটাই থাকে। নিচের কোডে প্রবলেমটা দেখাচ্ছি:

ছবি ২: ইনপুট স্ট্রিংয়ে নিউলাইন থাকায় regex আর কাজ করে না 

  

 পরে  https://regex101.com/ ওয়েবসাইটে গিয়ে টেস্ট করে দেখি আমার regex প্যাটার্ন আসলেই কাজ করে না, ঐখানেই বাগের হিন্টটা পেলাম যে .* -এ এক্সসেপশন আছে, নিচের ছবিতে: 

ছবি ৩: বাগের হিন্ট 

অর্থাৎ, .* আসলে 'any number of single character(s)' ডিটেক্ট করে, নিউলাইন (\n) ডিটেক্ট করতে পারে না। পরে সলুশনটা StackOverflow'র এই লিংক থেকে পেলাম: .* 'র পরিবর্তে [\s\S]* ব্যবহার করলে সবগুলোই ক্যারেক্টার কভার হয় [\s ='any whitespace character'; \S = 'any non-whitespace character' ]; তখন নিউলাইন (\n) ক্যারেক্টার, যেটা আসলে একটা whitespace character - সেটাও কভার হয়। আর regex -এ স্কোয়ার ব্রাকেট [ ] দিয়ে যেকোনোটা বোঝায়। কাজেই যেকোনো ক্যারেক্টার হলেই সেটা কভার করবে। নিচে বাগ ফিক্স করা কোডটা দিলাম:

ছবি ৪: বাগ ফিক্স করা সঠিক কোড 

সবশেষ: সামান্য এই বাগটা ফিক্স করে ভালো লেগেছে কারণ নতুন একটা জিনিস শিখলাম। আরো ভালো লেগেছে যখন টীম মিটিংয়ে এই বাগটা নিয়ে আলাপ করার পর, সবচেয়ে সিনিয়র ইঞ্জিনিয়ার'ও আমাকে কোনোরকম ভাব না নিয়ে বললো যে সেও আজকে প্রথম ব্যাপারটা জানলো! 

রেপ্লিটে উপরের কোডের লিংক: এই লিংকে  




কাজের জায়গায় ভুল থেকে শেখা: regex 'র একটা খুব কমন বিষয় যেটা এতদিন ভুল জানতাম

কাজের জায়গায় ভুল থেকে শেখা: regex 'র একটা খুব কমন বিষয় যেটা এতদিন ভুল জানতাম  ৩ ফেব্রুয়ারি, শনিবার, ২০২৪ রেগুলার এক্সপ্রেশন (Regular Exp...