End of File (EOF)

Apr 24, 2013

অনেকের মধ্যেই দেখেছি এই End of File বা EOF নিয়ে এক ধরনের আতংক কাজ করে! তবে এত আতংকিত হওয়ার কিছু নেই। এটা তেমন কিছুই না। সোজা বাংলা কথায় বলতে গেলে EOF হল ইনপুট নেওয়ার শেষ নির্দেশক কোন একটা সংকেত যেটি পাওয়ার পর প্রোগ্রাম আর কোন ইনপুট নিবে না।

শুরুতে প্রবলেমে এটা কিভাবে উল্লেখ করা থাকে দেখা যাক। জনপ্রিয় অনলাইন জাজ UVa এর ১০০৫৫ নাম্বার প্রবলেম “Hashmat the brave warrior” কথা যদি বলি তাহলে সেখানে দেখা যাবে বলা আছে, “Input is terminated by End of File”। কথাটার মানে হল, প্রবলেমটির জন্য যে ইনপুট সেট আছে সেখানে অনেকগুলো ইনপুট দেওয়া আছে। সেই ইনপুটগুলোর কোন এক জায়গায় EOF এর ইন্সট্রাকশন দেওয়া আছে। প্রোগ্রাম যতক্ষণ পর্যন্ত EOF না পাবে ততক্ষণ পর্যন্ত ইনপুট নিতে থাকবে। EOF পেয়ে গেলে প্রোগ্রাম শেষ হবে। বিভিন্ন অপারেটিং সিস্টেমে EOF কে বিভিন্নভাবে নির্দেশ করা হয়। উইন্ডোজে ctrl + z চাপ দিয়ে EOF এর কমান্ড দেওয়া হয়।

উপরের উদাহরণের প্রবলেমটিতে বলে দেওয়া আছে EOF আসলে প্রোগ্রাম শেষ হবে। কিছু প্রবলেমে বলা থাকে শুন্য আসলে ইনপুট নেওয়া শেষ করতে হবে। কোথাও বলা থাকে ঋণাত্মক সংখ্যা আসলে ইনপুট নেওয়া বন্ধ হবে। প্রবলেমভেদে কিভাবে ইনপুট নেওয়া শেষ হবে সেটা ভিন্ন হয়। তবে এমন কিছু প্রবলেম আছে যেখানে বলাই থাকে না যে কখন ইনপুট নেওয়া শেষ হবে। এরকম যদি হয় যে কখন ইনপুট নেওয়া শেষ করতে হবে, অর্থাৎ প্রোগ্রামটি কখন রান করা শেষ হবে সে বিষয়ে কিছুই বলা নেই তখন ধরে নিতে হয় যে EOF দিয়েই প্রোগ্রাম রান করা শেষ হবে।

এখন একটু গভীরতর কথায় যাওয়া যাক। ভয় পাওয়ার কিছু নাই, খুব বেশি গভীর না, হালকা গভীর, হাটু পর্যন্ত! :P EOF আমরা কিভাবে ব্যবহার করব? তবে তার আগে জানতে হবে আমরা একটি প্রোগ্রাম বারবার ইনপুট নিয়ে কিভাবে চালাব? সহজ উত্তর, লুপ দিয়ে। আমরা জানি একই ধরনের কাজ বার বার করার জন্য লুপ নামে সুন্দর একটি জিনিস আছে। Hashmat এর প্রবলেমটার কথাই ধরা যাক। প্রবলেমে বলা আছে প্রতিবার ২ টা করে সংখ্যা ইনপুট নিতে হবে এবং সেই ইনপুটের জন্য কাজ করতে হবে। কাজ হলে আউটপুট দেখাবে। এভাবে একবার কাজটি হবে। এরপর আবার ২ টা সংখ্যা ইনপুট নিতে হবে, এবং সমগ্র কাজের পুনরাবৃত্তি করতে হবে। এই বার বার ইনপুট নেওয়ার বিষয়টি আমরা যেভাবে লিখব সেটা নিচের চিত্রে দেখানো হল।

while( scanf( "%llu %llu", &a, &b ) ) {
    /* বাকি নির্দেশনা */
}

যেহেতু আমরা একই ধরনের কাজ বারবার করব, সেটার জন্য আমরা while লুপ ব্যবহার করেছি। তবে এখানে একটা অদ্ভুত জিনিস দেখা যাচ্ছে। সেটা হল, while এর পরে ব্র্যাকেটের মধ্যে যেখানে আমাদের শর্ত লেখার কথা ছিল, সেখানে আমরা scanf( ) ফাংশন ব্যবহার করেছি! এখন আসা যাক, কিভাবে এটা কাজ করে সেই প্রসঙ্গে।

আমরা জানি, কম্পিউটার সবকিছুকেই 1 এবং 0 তে রূপান্তর করে নিয়ে কাজ করে। যেকোন ধরনের শর্তকে যখন আমরা লিখি, সেই শর্তটি থেকে দুটি ঘটনা ঘটতে পারে, ১। শর্তটি সত্য, ২। শর্তটি মিথ্যা। শর্তটি যতই জটিল হোক না কেন, সেটি নিয়ে সকল প্রকার হিসাব নিকাশ করে শেষ পর্যন্ত সত্য বা মিথ্যা, এই দুইটা সিদ্ধান্তের যেকোন একটাতেই এসে উপনীত হয় কম্পিউটার। এখন এই সত্য বা মিথ্যাকেও সংখ্যায় প্রকাশ করতে হবে যাকে পরবর্তিতে কম্পিউটার বাইনারিতে রূপান্তর করবে। কারণ সে বাইনারি ছাড়া কিছু বুঝে না। তাই কম্পিউটার যেটা করে সেটা হল সে সকল প্রকার মিথ্যাকে 0 দিয়ে প্রকাশ করে এবং সকল সত্য শর্তকে 0 ব্যতীত অন্য যেকোন সংখ্যা দিয়ে প্রকাশ করে। অতএব মিথ্যা হলে মেমরিতে সকল বিট শুন্য হয়ে যায়। যদি কোন একটি বিটেও শুন্য না থাকে তাহলে সে সেটাকে সত্য ধরে নেয়। অতএব উপরে প্রোগ্রামের যে অংশটুকু দেওয়া আছে সেটি দেখে এটুকু ইতোমধ্যে বুঝে যাওয়ার কথা যে প্রোগ্রামটি চলতে হলে শর্তটি সত্য হতে হবে, অর্থাৎ শর্তের স্থানে কোন একটি অশুন্য নাম্বার থাকতে হবে। সেই অশুন্য নাম্বারটি আসবে scanf( ) ফাংশন থেকে। এখন সেটি দেখার জন্য আরও একটু গভীরে যাওয়া যাক, বেশি না, কোমর পর্যন্ত গভীর! :P

আমরা জানি ফাংশন দুই ধরনের হতে পারে, এক ধরনের ফাংশন কোন একটি মান return করে, আরেকধরনের ফাংশন সেটি করে না। scanf( ) ফাংশনটি C এর একটি স্ট্যান্ডার্ড ফাংশন। এটি একটি মান return করে। এখন কথা হল, সেই মানটি কত। scanf( ) ফাংশনের সাহায্যে যতগুলো ডাটা ইনপুট নেওয়ার কথা, সে যদি ততগুলো ডাটা সঠিকভাবে ইনপুট পায় তাহলে তত return করবে। অর্থাৎ উপরের প্রোগ্রামটিতে দেখা যাচ্ছে, scanf( "%llu %llu", &a, &b ) ফাংশনটির দুইটি ডাটা ইনপুট নেওয়ার কথা। সে যদি এই দুইটি ডাটা সঠিকভাবে ইনপুট পায় তাহলে 2 return করবে। এক্ষেত্রে তাকে সঠিক ডাটা টাইপের ডাটা ইনপুট পেতে হবে। সঠিক বলতে, যদি %d লেখা থাকে, তার মানে তাকে একটা নাম্বার ইনপুট দিতে হবে। যদি আমি a/b/c/d/?/!/* ইত্যাদি ইনপুট দেই তাহলে হবে না। অর্থাৎ যে ধরনের ডাটা যতগুলো ইনপুট নেওয়ার কথা ততগুলো যদি সঠিকভাবে ইনপুট পায় তাহলে তত return করবে।

যদি সে সঠিকভাবে ততগুলো ইনপুট না পায় তাহলে কি হবে? তাহলে যদি দুইটি ইনপুট নেওয়ার কথা থাকে এবং সে একটি ঠিকমত পায়, আরেকটি না পায়, তাহলে 1 return করবে। এখানে বলে রাখি, যদি return ব্যাপারটি কি সেই বিষয়ে সমস্যা থাকে তাহলে আপাতত ধরে নিন যেখানে ফাংশনটি লেখা আছে সেখানে ফাংশনটির জায়গায় একটা মান বসে যাবে। যেমন scanf( "%llu %llu", &a, &b ) যখন 2 return করবে তখন এরকম হয়ে যাবে - while( 2 ) । বিষয়টি বাস্তবে এমন না, তবে আপাতত ধরে নিন। তাহলে কি দাঁড়ালো? ফাংশনটি তার কাজ করল, এরপর একটা মান return করল। যেহেতু ফাংশনটি while এর শর্তের স্থানে লেখা আছে তাই সেই মানটি শর্ত হিসেবে ব্যবহৃত হবে। আর যেহেতু মানটি 0 না, তাই শর্তটি সত্য বলে গণ্য হবে এবং ভিতরের স্টেটমেন্টগুলো সম্পাদিত হবে।

এখন আস্তে আস্তে আবার অগভীরে যাওয়া যাক। এই কথাগুলো থেকে বুঝে যাওয়ার কথা কিভাবে while এর শর্তের স্থানে scanf( ) বা অন্য কোন ফাংশন কাজ করবে। কিন্তু যেটি হল এখানে, তা হল প্রতিবার ইনপুট নিবে, শর্ত সত্য হবে, প্রোগ্রাম রান করবে, এভাবে চলতেই থাকবে। ফলে জাজ আমাদেরকে Time Limit Exceeded নামক একটি verdict দিবে। যেহেতু প্রোগ্রামটি তার নির্ধারিত সময়ের মধ্যে থামতে পারেনি তাই এমনটি হবে। তাহলে থামাবো কিভাবে? বলেই দেওয়া আছে, EOF আসলে থামাতে হবে। আবার সেই প্রশ্নে চলে আসি, EOF ব্যবহার করব কিভাবে?

EOF হল একটি ম্যাক্রো। এটার মান সাধারনভাবে -1 ঠিক করা থাকে। ম্যাক্রো কি জিনিস সেই আলোচনায় না যাই। ধরে নেয়া যাক, EOF হল প্রোগ্রাম থামানোর একটা কমান্ড। আগেই বলেছি scanf( ) ফাংশনটি যতগুলো ইনপুট পাওয়ার কথা ততগুলো যদি সঠিকভাবে পায় তাহলে তত return করে। উপরের উদাহরণ থেকে দেখা যাচ্ছে, scanf( ) ফাংশনটির ২টি সংখ্যা ইনপুট পাওয়ার কথা। EOF পাওয়া মানে, EOF ইনপুট দেওয়া হয়েছে। অর্থাৎ ফাংশনটি তখন ২টি সংখ্যা ঠিকমত ইনপুট পায়নি, সেখানে EOF ইনপুট পেয়েছে। EOF ইনপুট পেলে scanf( ) এর একটি বিশেষত্ব হল যে সে EOF এর মান -1 return করে। অর্থাৎ তার যা return করার কথা তার বদলে অন্য একটা মান EOF এর মান return করছে। এই কনসেপ্ট আমাদেরকে ব্যবহার করতে হবে। দেখা যাক এটা ব্যবহার করে কিভাবে প্রোগ্রামটি লেখা যায়।

/* ১ম উপায় */
while( scanf( "%llu %llu", &a, &b ) != EOF ) {
    /* বাকি নির্দেশনা */
}
/* ২য় উপায় */
while( scanf( "%llu %llu", &a, &b ) == 2 ) {
    /* বাকি নির্দেশনা */
}

প্রথম উপায়টি দেখা যাক। যখন আমরা শর্তের ভিতরে লিখছি scanf( … ) != EOF তখন যেটা হয় তা হল, scanf( ) তার কাজ করবে। করে একটা মান return করবে। সেই মান কি EOF এর সমান নাকি তা দেখা হবে। যদি সমান না হয় তাহলে শর্ত সত্য বিবেচিত হবে। অতএব যতবার scanf( ) ঠিকমত ইনপুট নিবে ততবার তার একটা ধনাত্মক মান পাওয়া যাবে এবং সেটি EOF এর সমান না হওয়ার প্রোগ্রাম চলতে থাকবে। এখন যখনই EOF আসবে, তখন scanf( ) ফাংশন EOF এর মান return করবে, বেশিরভাগ ক্ষেত্রে যেটি থাকে -1 । এই মানটি EOF এর মান বলে তা অবশ্যই EOF এর সমান হবে, অর্থাৎ শর্ত মিথ্যা হয়ে যাবে। প্রোগ্রাম আর রান করবে না।

দ্বিতীয় উপায়টিতেও একই কথা, এখানে আমাদের ২টি সংখ্যা ইনপুট পাওয়ার কথা। সঠিকভাবে সেটি ইনপুট পেলে scanf( ) ফাংশন 2 return করবে। তাহলে 2 == 2 হবে এবং শর্ত সত্য হবে। যখনই সে EOF পাবে তখনই -1 return করবে। ফলে -1 == 2 শর্তটি মিথ্যা হয়ে যাবে। এতে করে প্রোগ্রাম আর রান করবে না।

আশা করি এই লেখা দিয়ে EOF এবং তার ব্যবহার সম্পর্কে মোটামুটি একটা ধারণা দিতে পেরেছি। EOF এর বেসিক ব্যবহার সম্পর্কে আশা করি আর কোন কনফিউশন থাকবে না। থাকলে নিঃসংকোচে প্রশ্ন করতে পারেন। :)