স্ট্রিং part 1


স্ট্রিং (String) ডাটা স্ট্রাকচার -  ডাটা স্ট্রাকচার, স্ট্রিং
 এক বা একাধিক character মিলে string তৈরি হয়। সোজা কথায় স্ট্রিং হচ্ছে ক্যারেক্টার টাইপের অ্যারে। তবে প্রোগ্রামিংয়ে এটির ব্যবহার এতই বেশি যে কোনো কোনো ল্যাঙ্গুয়েজে স্ট্রিংকে আলাদা একটি ডাটা টাইপ হিসেবে ধরা হয়। তবে সি-তে আমরা char টাইপের অ্যারে দিয়েই স্ট্রিংয়ের কাজ করব।
নিচের উদাহরণগুলো লক্ষ করুনঃ 
char country[11] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', '\0'};   
char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', '\0'};   
char country[] = "Bangladesh";   
char *country = "Bangladesh";


এভাবে আমরা স্ট্রিং ডিক্লেয়ার করতে পারি। চারটি ডিক্লারেশন আসলে একই জিনিস। সবার শেষে একটি Null character ('\0') দিলে কম্পাইলার বুঝতে পারে এখানেই স্ট্রিংয়ের শেষ। আবার তৃতীয় উদাহরণে অ্যারের উপাদানগুলো আলাদা করে লেখা হয়নি, একসঙ্গে লেখা হয়েছে। ক্ষেত্রে কম্পাইলার নিজেই Null character বসিয়ে নেবে। চতুর্থ উদাহরণটি একটু অদ্ভুত। এখানে যে জিনিসটা ব্যবহার করা হয়েছে তার নাম পয়েন্টার (pointer) পয়েন্টার নিয়ে পরে আমরা বিস্তারিত আলোচনা করবো।
এবারে প্রোগ্রাম লিখার পালা।
#include <stdio.h>    
 int main()    
 {    
     char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', '\0'};    
     printf("%s\n", country);    
     return 0;    
 }


স্ট্রিং (String) ডাটা স্ট্রাকচার -  ডাটা স্ট্রাকচার, স্ট্রিং
আমাদের এবারের প্রোগ্রাম হবে দুটি স্ট্রিংয়ের মধ্যে তুলনা করা। অর্থাৎ দুটি স্ট্রিংয়ের মধ্যে ছোট, বড়, সমান নির্ণয় করা। সংখ্যার ক্ষেত্রে যেমন >, <, >=, <=, == চিহ্ন ব্যবহার করে তুলনা করা যায়, স্ট্রিংয়ের ক্ষেত্রে সেই ব্যবস্থা নাই। কিন্তু স্ট্রিংয়ের ক্ষেত্রে প্রায়ই আমাদের এই তুলনা করার দরকার পড়বে। যেমন ধরুন, সর্টিংয়ের ক্ষেত্রে যেখানে ছোট থেকে বড় বা বড় থেকে ছোট ক্রমানুসারে সাজাতে হবে (alphabetical ordering) স্ট্রিংয়ে ছোট-বড় আবার কী? বেশি কথা বলে ব্যাখ্যা না করে কিছু উদাহরণ দিই, তাহলেই বুঝতে পারবে। 'aaa'-এর চেয়ে 'aab' বড়। আবার 'ba' 'ca'-এর মধ্যে 'ca' বড়। এই প্রোগ্রামে আমরা একটি ফাংশন লিখব string_compare() যেটির কাজ হবে দুটি স্ট্রিংয়ের মধ্যে তুলনা করে প্রথমটি দ্বিতীয়টির চেয়ে বড় হলে 1 রিটার্ন করবে, ছোট হলে -1 আর দুটি সমান হলে 0 রিটার্ন করবে। ফাংশনের রিটার্ন টাইপ হবে ইন্টিজার এবং প্যারামিটার হবে দুটি char টাইপের অ্যারে।
int string_compare(char a[], char b[])   
{   

}


আমাদের মূল কাজ হবে a-এর প্রথম উপাদানের সঙ্গে b-এর প্রথম উপাদান, a-এর দ্বিতীয় উপাদানের সঙ্গে b-এর দ্বিতীয় উপাদান এভাবে তুলনা করতে থাকা। যখনই a-এর কোনো উপাদান b-এর কোনো উপাদানের চেয়ে ছোট হবে, আমরা সঙ্গে সঙ্গে বলে দিতে পারি যে a, b-এর চেয়ে ছোট। সুতরাং -1 রিটার্ন করে ফাংশন থেকে বের হয়ে আসব। একইভাবে যখনই a-এর কোনো উপাদান b-এর কোনো উপাদানের চেয়ে বড় হবে, সঙ্গে সঙ্গে 1 রিটার্ন করে ফাংশন থেকে বের হয়ে আসব কারণ a, b-এর চেয়ে বড়। কিন্তু যদি সবগুলোই সমান হয়? তখন আমরা 0 রিটার্ন করব। তাতে বুঝব যে স্ট্রিং দুটি সমান।
int string_compare(char a[], char b[])    
 {    
     int i, j;    
     for(i = 0; a[i] != '\0' && b[i] != '\0'; i++) {    
         if(a[i] < b[i]) {    
             return -1;    
         }    
         if(a[i] > b[i]) {    
             return 1;    
         }    
     }    
     if(string_length(a) == string_length(b)) {    
         return 0;    
     }    
     if(string_length(a) < string_length(b)) {    
         return -1;    
     }    
     if(string_length(a) > string_length(b)) {    
         return 1;    
     }    
 }


স্ট্রিংয়ের বেসিক জিনিসগুলো নিয়ে আলোচনা করলাম। তবে মজার ব্যাপার হচ্ছে সি ল্যাঙ্গুয়েজে একটি হেডার ফাইল আছে, যার নাম string.h এবং ওইখানে বেশিরভাগ স্ট্রিং-সংক্রান্ত কাজের জন্য ফাংশন তৈরি করে দেওয়া আছে (যেমন: strcmp, strlen, strcpy ইত্যাদি)




স্ট্রিং (String) ডাটা স্ট্রাকচার -  ডাটা স্ট্রাকচার, স্ট্রিং
এখন আমরা আরেকটি প্রোগ্রাম লিখব যেটি ইনপুট হিসেবে একটি স্ট্রিং নেবে (যেখানে অনেকগুলো শব্দ থাকবে) এই স্ট্রিংয়ের সর্বোচ্চ দৈর্ঘ্য হবে 1000 শব্দগুলোর মাঝখানে এক বা একাধিক স্পেস থাকবে। আউটপুট হিসেবে প্রতিটি শব্দ আলাদা লাইনে প্রিন্ট করতে হবে। বিরামচিহ্নগুলো (punctuation) প্রিন্ট করা যাবে না এবং শব্দের প্রথম অক্ষর হবে বড় হাতের।

অনেক শর্ত দিয়ে ফেললাম। তবে প্রোগ্রামটি খুব কঠিন কিছু নয়। নিজে নিজে চেষ্টা করে দেখুন আর না পারলে এখন চলুন দেখি কীভাবে সমাধান করা যায়।

প্রথম কথা হচ্ছে, ইনপুট নেব কীভাবে? বুঝতেই পারছেন যে ইনপুটে যেহেতু স্পেস থাকবে, scanf("%s") ব্যবহার করা যাবে না। তাই আমরা gets() ব্যবহার করব। তার পরের কথা হচ্ছে একটি শব্দে কোন কোন ক্যারেক্টার থাকতে পারে? যেহেতু বলা নেই, আমরা ধরে নিই 'a' থেকে 'z', 'A' থেকে 'Z' আর '0' থেকে '9' থাকবে।

তার পরের প্রশ্ন হচ্ছে, আমরা কখন বুঝব বা আমাদের প্রোগ্রামকে কীভাবে বোঝাবো যে একটি শব্দ শুরু হয়েছে?-এর জন্য আমরা একটি ভেরিয়েবল রাখতে পারি। ভেরিয়েবলের নাম যদি দিই is_word_started তাহলে এর মান 0 হলে বুঝব শব্দ শুরু হয়নি, শব্দ শুরু হলে এর মান আমরা 1 করে দেব। আবার শব্দ শেষ হলে 0 করে দেব। যখন দেখব শব্দ শুরু হয়ে গেছে (is_word_started-এর মান 1) কিন্তু কোনো ক্যারেক্টারের মান 'a' – 'z' বা 'A' – 'Z', বা '0' – '9' এই রেঞ্জের মধ্যে নেই, তখনই বুঝব শব্দটি শেষ। 

#include <stdio.h>   
 #include <string.h>   
 int main()   
 {   
     char s[1002], word[100];   
     int i, j, length, is_word_started;   
     gets(s);   
     length = strlen(s);   
     is_word_started = 0;   
     for (i = 0, j = 0; i < length; i++) {   
         if (s[i] >= 'a' && s[i] <= 'z') {   
             if (is_word_started == 0) {   
                 is_word_started = 1;   
                 word[j] = 'A' + s[i] - 'a'; // first character is capital   
                 j++;   
             }   
             else {   
                 word[j] = s[i];   
                 j++;   
             }   
         }   
         else if (s[i] >= 'A' && s[i] <= 'Z') {   
             if (is_word_started == 0) {   
                 is_word_started = 1;   
             }   
             word[j] = s[i];   
             j++;   
         }   
         else if (s[i] >= '0' && s[i] <= '9') {   
             if (is_word_started == 0) {   
                 is_word_started = 1;   
             }   
             word[j] = s[i];   
             j++;   
         }   
         else {   
             if (is_word_started == 1) {   
                 is_word_started = 0;   
                 word[j] = '\0';   
                 printf("%s\n", word);   
                 j = 0;   
             }   
         }   
     }   
     return 0;   
 }


ইনপুট হিসেবে যেকোনো কিছু লিখতে পারেন। যেমন: This is a test. আউটপুট কী?

আউটপুট হচ্ছে এই রকম:

This
Is
A

কী মুশকিল! test গেল কোথায়?

এখন আপনার কাজ হবে test-এর নিখোঁজ হওয়ার রহস্যটা তদন্ত করা। তারপর আমি প্রোগ্রামটি ব্যাখ্যা করব।

দেখুন প্রোগ্রামে আমি স্ট্রিংয়ের দৈর্ঘ্য নির্ণয়ের জন্য strlen ফাংশন ব্যবহার করেছি। আর-এর জন্য আমাকে string.h হেডার ফাইলটি include করতে হয়েছে। ইনপুট হিসেবে স্ট্রিংটা নিলাম s-এ। আর word রাখার জন্য একটি অ্যারে ডিক্লেয়ার করে রেখেছি। তারপর আমি i = 0 থেকে length পর্যন্ত একটি লুপ চালিয়েছি s-এর ভেতরের প্রতিটি ক্যারেক্টার পরীক্ষা করার জন্য।

if (s[i] >= 'a' && s[i] <= 'z') দিয়ে পরীক্ষা করলাম এটি ছোট হাতের অক্ষর নাকি। যদি ছোট হাতের অক্ষর হয় তবে একটি শব্দের প্রথম অক্ষর কি না সেটি জানতে হবে। কারণ প্রথম অক্ষর হলে ওটাকে আবার বড় হাতের অক্ষরে রূপান্তর করতে হবে। সেই পরীক্ষাটা আমরা করেছি: if (is_word_started == 0) দিয়ে। এটি সত্য হওয়া মানে শব্দ শুরু হয়নি, এটিই প্রথম অক্ষর। তাই আমরা is_word_started-এর মান 1 করে দেব। আর word[j]তে s[i]-এর বড় হাতের অক্ষরটা নেব। তারপর j-এর মান এক বাড়াতে হবে। else if (s[i] >= 'A' && s[i] <= 'Z') এবং else if (s[i] >= '0' && s[i] <= '9') এই দুটি শর্তের ভেতরেই আমরা একই কাজ করি। s[i]কে word[j]তে কপি করি। তাই চাইলে দুটি শর্তকে একসঙ্গে এভাবেও লিখতে পারতাম: else if ((s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= '0' && s[i] <= '9')) তার পরের else-এর ভেতরে ঢোকার মানে হচ্ছে আগের if এবং else if-এর শর্তগুলো মিথ্যা হয়েছে। তাই s[i]-এর ভেতরে যেই ক্যারেক্টার আছে সেটি word- রাখা যাবে না। এবং যদি word ইতিমধ্যে শুরু হয়ে গিয়ে থাকে, সেটি শেষ করতে হবে এবং wordটি প্রিন্ট করতে হবে। আর যদি word শুরু না হয়ে থাকে তাহলে কিছু করার দরকার নেই।

else {   
     if (is_word_started == 1) {   
         is_word_started = 0;   
         word[j] = '\0';   
         printf("%s\n", word);   
         j = 0;   
     }   
 }










মন্তব্যসমূহ