ছোটদের প্রোগ্রামিং শেখা এখন আগের চেয়েও সহজ। কারণ দ্বিমিক প্রকাশনী থেকে বের হয়েছে তামিম শাহরিয়ার সুবিন-এর "পাইথন দিয়ে প্রোগ্রামিং শেখা"! বিস্তারিত জানতে এখানে ক্লিক করুন

[প্রোগ্রামিং বইঃ অধ্যায় নয়] স্ট্রিং (string)।

তোমরা যারা 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;     
 }    
 প্রোগ্রাম: ৯.১    

এখানে লক্ষ করো যে printf-এর ভেতরে %s ব্যবহার করা হয়েছে স্ট্রিং প্রিন্ট করার জন্য। আর অ্যারেতে শেষের '\0'টা ব্যবহার না করলেও চলে আসলে। বর্তমানের কম্পাইলারগুলো এটি বুঝে নিতে পারে।

 #include <stdio.h>     
 int main()     
 {     
     char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', ' ', 'i', 's', ' ', 'm', 'y', ' ', 'c', 'o', 'u', 'n', 't', 'r', 'y'};     
     printf("%s\n", country);     
     return 0;     
 }    
 প্রোগ্রাম: ৯.২    

প্রোগ্রামটি চালাও। তারপর নিচের প্রোগ্রামটি চালাও। আউটপুটে কি পার্থক্য দেখতে পাচ্ছ? পার্থক্যের কারণটা কী?

 #include <stdio.h>     
 int main()     
 {     
     char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h', '\0', 'i', 's', ' ', 'm', 'y', ' ', 'c', 'o', 'u', 'n', 't', 'r', 'y'};     
     printf("%s\n", country);        
     return 0;     
 }    
 প্রোগ্রাম: ৯.৩    

পার্থক্যটা কী সেটি তোমরা প্রোগ্রাম দুটি কম্পিউটারে চালালেই বুঝবে। পার্থক্যের কারণ হচ্ছে দ্বিতীয় প্রোগ্রামে স্ট্রিংয়ের ভেতরে এক জায়গায় '\0' থাকায় কম্পাইলার ধরে নিচ্ছে ওখানে স্ট্রিংটা শেষ হয়ে গেছে।

এবারে আমরা একটি প্রোগ্রাম লিখব। একটি স্ট্রিংয়ের ভেতরের সব অক্ষরকে বড় হাতের অক্ষরে (অর্থাৎ capital letter বা uppercase character) রূপান্তর করা। তবে এর জন্য আমাদের একটি জিনিস জানতে হবে। প্রতিটি অক্ষরের বিপরীতে কম্পিউটার একটি সংখ্যার কোড ব্যবহার করে। সেই কোড অনুযায়ী, 'A'-এর মান হচ্ছে 65, 'B'-এর মান হচ্ছে 66, 'C'-এর মান হচ্ছে 67... এভাবে 'Z'-এর মান হচ্ছে 90। তেমনই 'a' হচ্ছে 97, 'b' হচ্ছে 98 ... এভাবে 'z' হচ্ছে 122। সুতরাং কোনো ক্যারেক্টার বড় হাতের কি না সেটি আমরা নির্ণয় করতে পারি এভাবে: if(ch >= 'A' && ch <= 'Z') অথবা if(ch >= 65 && ch <= 90)। তেমনই ছোট হাতের অক্ষরের জন্য: if(ch >= 'a' && ch <= 'z') অথবা if(ch >= 97 && ch <= 122)। এখন কোনো ক্যারেক্টার যদি ছোট হাতের হয়, তবে তাকে বড় হাতের অক্ষরে রূপান্তর করার উপায় কী? উপায় খুব সহজ। একটি উদাহরণ দেখো: char ch = 'c'; ch = 'A' + (ch – 'a'); এখানে যেটি হচ্ছে, প্রথমে ch থেকে 'a' বিয়োগ করা হচ্ছে মানে 'c' থেকে 'a' বিয়োগ (আসলে 99 থেকে 97 বিয়োগ হচ্ছে)। বিয়োগফল 2। এবারে 'A'-এর সঙ্গে যদি ওই 2 যোগ করে দিই তবে সেটি 'C' হয়ে যাবে! এখন প্রোগ্রামটি লিখে ফেলা যাক: 

 #include <stdio.h>     
 int main()     
 {     
     char country[] = {'B', 'a', 'n', 'g', 'l', 'a', 'd', 'e', 's', 'h'};     
     int i, length;     
     printf("%s\n", country);     
     length = 10;         
     for(i = 0; i < length; i++) {     
         if(country[i] >= 97 && country[i] <= 122) {     
             country[i] = 'A' + (country[i] - 'a');     
         }     
     }     
     printf("%s\n", country);     
     return 0;     
 }    
 প্রোগ্রাম: ৯.৪    

এখন তোমরা uppercase থেকে lowercase-এ রূপান্তরের প্রোগ্রামটি লিখে ফেলো। তারপরে আবার বইটি পড়া শুরু করো।

এখানে লক্ষ করো যে স্ট্রিংয়ে (ক্যারেক্টারের অ্যারেতে) মোট কয়টি উপাদান আছে সেটি আমি দেখেই লিখে ফেলেছি এবং সরাসরি বসিয়ে দিয়েছি length = 10।

এবার আমরা কোনো স্ট্রিংয়ের দৈর্ঘ্য মাপার জন্য একটি ফাংশন লিখব! এটি তেমন কঠিন কিছু নয়। একটি লুপের সাহায্যে স্ট্রিংয়ের প্রতিটি উপাদান পরীক্ষা করতে হবে এবং Null character ('\0') পেলে লুপ থেকে বের হয়ে যাবে অর্থাৎ, '\0' না পাওয়া পর্যন্ত লুপ চলতে থাকবে। আর লুপ যতবার চলবে স্ট্রিংয়ের দৈর্ঘ্যও তত হবে।

 #include <stdio.h>     
 int string_length(char str[])     
 {     
     int i, length = 0;     
     for(i = 0; str[i] != '\0'; i++) {     
         length++;     
     }     
     return length;    
 }     
 int main()     
 {     
     char country[100];     
     int length;     
     while(1 == scanf("%s", country)) {                
         length = string_length(country);     
         printf("length: %d\n", length);     
     }     
     return 0;     
 }    
 প্রোগ্রাম: ৯.৫    

ওপরের প্রোগ্রামটায় তোমরা দেখতে পাচ্ছ যে ইনপুট নেওয়ার জন্য scanf ফাংশন ব্যবহার করা হয়েছে এবং স্ট্রিং ইনপুট নেওয়ার জন্য %s ব্যবহৃত হয়েছে। scanf ফাংশনটি যতটি উপাদান ইনপুট হিসেবে নেয়, সেই সংখ্যাটি রিটার্ন করে। সাধারণত রিটার্ন ভ্যালুটি আমাদের দরকার হয় না, তাই scanf ব্যবহার করলেও আমরা ওই ভ্যালুটি রাখি না। যেমন দুটি ইন্টিজার ইনপুট নিতে গেলে আমরা লিখি: scanf("%d %d", &n1, &n2);। আমরা এটি চাইলে এভাবেও লিখতে পারতাম: value = scanf("%d %d", &n1, &n2);। তোমরা প্রিন্ট করলে দেখবে value-এর মান 2। while(1 == scanf("%s", country)) লাইনে যেটি ঘটছে তা হলো, যতক্ষণ একটি country-এর নাম scanf দিয়ে ইনপুট নেওয়া হচ্ছে, ফাংশনটি 1 রিটার্ন করছে, আর লুপের ভেতরের কন্ডিশন সত্য হচ্ছে (1 == 1), তাই লুপের কাজ চলতে থাকবে।

আরেকটি জিনিস খেয়াল করো যে country-এর আগে কোন & চিহ্ন ব্যবহার করা হয়নি। তোমরা &country লিখে দেখো প্রোগ্রামটি কী আচরণ করে। তবে %s ব্যবহারের একটি সমস্যা হচ্ছে স্ট্রিংয়ে কোনো হোয়াইটস্পেস ক্যারেক্টার (যেমন: স্পেস, ট্যাব ইত্যাদি) থাকা যাবে না, এমন কিছু পেলে scanf ওই ক্যারেক্টার পর্যন্ত একটি স্ট্রিং ধরে নেয়। যেমন, ইনপুট যদি হয় this is তবে scanf প্রথমে thisকেই স্ট্রিং হিসেবে নেবে, তারপরে যদি আবার scanf ফাংশন কল করা হয়, তবে isকে সে স্ট্রিং হিসেবে ইনপুট নিয়ে নেবে। এই সমস্যা এড়ানোর জন্য আমরা gets ফাংশন ব্যবহার করতে পারি। নিচের উদাহরণটি দেখো:

 #include <stdio.h>     
 int main()     
 {     
     char ara[100];     
     while(NULL != gets(ara)) {                     
         printf("%s\n", ara);     
     }     
     return 0;     
 }    
 প্রোগ্রাম: ৯.৬    

এই প্রোগ্রামটিও চলতে থাকবে যতক্ষণ না তুমি ctrl + z (মানে কি-বোর্ডে ctrl ও z একসঙ্গে) চাপো, লিনাক্সের জন্য ctrl + d। ctrl + z বা ctrl + d দিলে gets ফাংশনটি NULL রিটার্ন করে। আরেকটি জিনিস লক্ষ করো যে আমি char ara[100]; ডিক্লেয়ার করে শুরুতেই বলে দিয়েছি স্ট্রিংয়ের সর্বোচ্চ দৈর্ঘ্য হবে 100।

আরেকটি ব্যাপার। string_length ফাংশনের ভেতরে আসলে দুটি ভেরিয়েবল ব্যবহার না করলেও চলে। আমরা ফাংশনটি এভাবেও লিখতে পারি:

 int string_length(char str[])     
 {     
     int i;         
     for(i = 0; str[i] != '\0'; i++);             
     return i;     
 }     

এখন তোমাদের কাজ হবে string_length ফাংশনটি for লুপ ব্যবহার না করে while লুপ ব্যবহার করে লেখা।

আমাদের পরবর্তী প্রোগ্রামের লক্ষ্য হবে দুটি স্ট্রিং জোড়া দেওয়া বা concatenate করা। যেমন একটি স্ট্রিং যদি হয় "bangla" এবং আরেকটি স্ট্রিং যদি হয় "desh" তবে দুটি জোড়া দিয়ে "bangladesh" বানাতে হবে।

প্রথমেই স্ট্রিংগুলো ডিক্লেয়ার করে নেই: char str1[] = "bangla", str2[] = "desh", str3[12];

আমাদের লক্ষ হচ্ছে str3তে "bangladesh" রাখা। খুব সুবিধা হতো যদি আমরা এমন কিছু লিখতে পারতাম:
str3 = str1 + str2;

কিন্তু 'সি'-তে এভাবে দুটি অ্যারে বা স্ট্রিং যোগ করা যায় না। তাই একটি একটি করে str1-এর উপাদানগুলো str3তে কপি করতে হবে, তারপর str2-এর উপাদানগুলো str3তে কপি করতে হবে।

 #include <stdio.h>     
 int main()     
 {     
     char str1[] = "bangla", str2[] = "desh", str3[12];     
     int i, j, length1 = 6, length2 = 4;     
     for(i = 0, j = 0; i < length1; i++, j++) {     
         str3[j] = str1[i];     
     }         
     for(i = 0, j = 0; i < length2; i++, j++) {     
         str3[j] = str2[i];     
     }     
     str3[j] = '\0';     
     printf("%s\n", str3);     
     return 0;     
 }    
 প্রোগ্রাম: ৯.৭    

প্রোগ্রামটি চালাও। আউটপুট কী আসা উচিত? bangladesh। কিন্তু আউটপুট এসেছে desh। আসলে আমরা কিছু একটা ভুল করেছি। তোমাদের এখন সেই ভুলটি ঠিক করার চেষ্টা করা উচিত। অন্তত তিরিশ মিনিট চেষ্টার পরও যদি ভুল বের করতে না পারো তবে আবার বইটি পড়া শুরু করো।

 for(i = 0, j = 0; i < length1; i++, j++) {     
     str3[j] = str1[i];     
 }    

এখানে আমরা শুরুতেই i-এর মান 0 করেছি কারণ iকে আমরা str1-এর ইনডেক্স হিসেবে ব্যবহার করব। jকে ব্যবহার করব str3-এর ইনডেক্স হিসেবে তাই j-এর মানও 0 করা হয়েছে। তারপর একে একে str1-এর উপাদানগুলো str3তে কপি করছি এবং i ও j-এর মান 1 করে বাড়াচ্ছি (i++, j++)। লুপ শেষ হওয়ার পরে i ও j প্রত্যেকের মান হবে 6।

এখন পরের লুপে আমরা str2কে str3-তে কপি করব। এখন str2-এর ইনডেক্স হিসেবে যদি i ব্যবহার করি, তবে তার মান লুপের শুরুতেই আবার 0 করে দিতে হবে। আমরা সেটি করেছি। কিন্তু ভুল করেছি সেই সঙ্গে j-এর মান 0 করে দিয়ে। j-এর মান 0 করলে তো str2-এর প্রথম (0তম) উপাদান str3-এর প্রথম (0তম) উপাদান হিসেবে কপি হবে, কিন্তু আমরা তো সেটি চাই না। আমরা চাই str2-এর প্রথম উপাদান হবে str3-এর সপ্তম উপাদান। তাহলে j-এর মান 0 করা যাবে না। তাই দ্বিতীয় লুপটি হবে এমন:

 for(i = 0; i < length2; i++, j++) {     
     str3[j] = str2[i];     
 }    

আরেকটি ব্যাপার লক্ষ করো। দ্বিতীয় লুপ থেকে বের হবার পরে str3-এর শেষ ঘরে '\0' অ্যাসাইন করেছি (str3[j] = '\0';) যাতে স্ট্রিংটা যে ওখানেই শেষ, এটি কম্পাইলার বুঝতে পারে।

আমাদের পরবর্তী প্রোগ্রাম হবে দুটি স্ট্রিংয়ের মধ্যে তুলনা করা। অর্থাৎ দুটি স্ট্রিংয়ের মধ্যে ছোট, বড়, সমান নির্ণয় করা। সংখ্যার ক্ষেত্রে যেমন >, <, >=, <=, == চিহ্ন ব্যবহার করে তুলনা করা যায়, স্ট্রিংয়ের ক্ষেত্রে সেই ব্যবস্থা নাই। কিন্তু স্ট্রিংয়ের ক্ষেত্রে প্রায়ই আমাদের এই তুলনা করার দরকার পড়বে। যেমন ধরো, সর্টিংয়ের ক্ষেত্রে যেখানে ছোট থেকে বড় বা বড় থেকে ছোট ক্রমানুসারে সাজাতে হবে (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 ইত্যাদি)। তোমাদের দিয়ে কাজগুলো আমি আবার করালাম বলে দুঃখ পাওয়ার কোনো কারণ নেই, আমার ওপর রাগ করারও কিছু নেই। মৌলিক জিনিসগুলো শিখে রাখা সব সময়ই গুরুত্বপূর্ণ, যা তোমার প্রোগ্রামিং চিন্তাকে বিকশিত করবে।

এখন আমরা আরেকটি প্রোগ্রাম লিখব যেটি ইনপুট হিসেবে একটি স্ট্রিং নেবে (যেখানে অনেকগুলো শব্দ থাকবে)। এই স্ট্রিংয়ের সর্বোচ্চ দৈর্ঘ্য হবে 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;    
 }     
 প্রোগ্রাম: ৯.৮    

প্রোগ্রামটি বুঝতে কি একটু সমস্যা হচ্ছে? সে পরে দেখা যাবে, আগে প্রোগ্রামটি চটপট কম্পিউটারে টাইপ করে ফেলো, কম্পাইল ও রান করো। যারা লিনাক্স ব্যবহার করছ তারা gets() ব্যবহারের কারণে কম্পাইলার থেকে একটি সতর্ক সংকেত (warning) পেতে পারো, পাত্তা দিয়ো না।

ইনপুট হিসেবে যেকোনো কিছু লিখতে পারো। যেমন: 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;    
     }    
 }    

তোমরা কি test-রহস্য সমাধান করতে পেরেছ? তোমরা চেষ্টা করতে থাকো আর আমি এখন প্রোগ্রামটি অন্যভাবে লিখব (এর সঙ্গে test রহস্যের কোনো সম্পর্ক নেই সেটি বলে রাখলাম)।

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

 #include <stdio.h>    
 #include <string.h>    
 int main()    
 {    
     char s[1002], ch;    
     int i, length, is_word_started;    
     gets(s);    
     length = strlen(s);    
     is_word_started = 0;    
     for (i = 0; i < length; i++) {    
         if (s[i] >= 'a' && s[i] <= 'z') {    
             if (is_word_started == 0) {    
                 is_word_started = 1;    
                 ch = 'A' + s[i] - 'a';    
                 printf("%c", ch);    
             }    
             else {    
                 printf("%c", s[i]);    
             }    
         }    
         else if ((s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= '0' && s[i] <= '9')) {    
             if (is_word_started == 0) {    
                 is_word_started = 1;    
             }    
             printf("%c", s[i]);    
         }    
         else {    
             if (is_word_started == 1) {    
                 is_word_started = 0;    
                 printf("\n");    
             }    
         }    
     }    
     printf("\n");    
     return 0;    
 }    
 প্রোগ্রাম: ৯.৯    

এখন প্রোগ্রামটি বুঝতে চেষ্টা করো এবং বিভিন্ন ইনপুট দিয়ে পরীক্ষা করে দেখো। যেমন:
This is test number 9.9

স্ট্রিং-সংক্রান্ত সমস্যাগুলো দেখতে জটিল মনে হলেও আসলে সহজ। আর এ ধরনের সমস্যা সমাধানের যত চর্চা করবে দক্ষতা তত বাড়বে।

১০টি মন্তব্য:

  1. ভাইয়া আমি একটা ছোট প্রোগ্রাম লিখতে চেষ্টা করছি কিন্তু পারছি না। আসলে ব্যপার টা হচ্ছে যখন return ইনপুট দেয়া হবে তখন লুপ থেকে বের হয়ে যাবে। আমার প্রোগ্রাম টাই return লিখলে লুপ থেকে বের হচ্ছে কিন্তু ৬ ক্যারেক্টার এর কিছু লিখলে তার দৈর্ঘ্য দেখাচ্ছে না !=৬ হলে আবার দৈর্ঘ্য দেখাচ্ছে । একটু সাহায্য করুন ।

    #include


    int str_length( char str[] ){
    int i,length = 0;
    for( i=0; str[i] != '\0'; i++){
    length++;
    }
    return length;
    }


    int main(){
    char name[20];
    printf(" Warning :: \"return\" is to break loop\n\n");
    while( 1==scanf("%s",&name)){
    int length = str_length(name);

    if(length!=6){
    printf(" The length of %s is %d\n",name,length);
    }

    else{

    int i,x=0;
    char ret[]="return";
    for( i=0; i<6; i++){
    if( name[i] == ret[i]){
    x++;
    }
    else{
    break;
    }
    }
    if(x==6){
    printf(" \t\tOops Bye Bye......\n");
    break;
    }
    }

    }
    return 0;
    }

    উত্তরমুছুন
  2. ভাইয়া প্রোগ্রাম ৯.৫ এর '' while(1 == scanf("%s", country)) { '' এটা বুঝতে পারছি না , নিচের লেখাটা পড়বার পর ও :( !! কি করব এখন

    উত্তরমুছুন
    উত্তরগুলি
    1. না বুঝলে অসুবিধা নাই। বইয়ের বাকী অংশ পড়তে থাকো। মোট দুই-তিনবার বইটা ঠিকভাবে পড়লে এবং যা যা করতে বলা হয়েছে সেই কাজগুলো করলে তোমার প্রোগ্রামিং বেসিক শক্ত হয়ে যাবে।

      মুছুন
    2. এখানে জিরো এবং ওয়ানের কাজ। অর্থাৎ যখন আপনি কোন ইনপুট দিবেন, তখন তা হবে ওয়ান। আর ঠিক তখন লুপের এই কন্ডিশন টা সত্য হবে ওয়ান ইজ ইকোয়াল টু ওয়ান। আর যখন আপনি ইনপুট দিবেন না, তখন তা হবে জিরো, তাহলে কন্ডিশন টি মিথ্যা হয়ে যাচ্ছে। অতঃপর তা লুপ থেকে বের হয়ে যাচ্ছে।

      মুছুন
  3. this is test, test কোথায় হারালো সেই রহস্যের সমাধান করলাম ,
    হয়েছে কিনা বলবেন দয়া করে।
    #include
    #include
    int main()
    {
    char str[1000], word[50];
    int i, j, is_word_started = 0;
    gets(str);
    int length = strlen(str);


    for(i = 0, j = 0; i <= length; i++)
    {
    if(str[i] >= 'a' && str[i] <= 'z')
    {
    if(is_word_started == 0)
    {
    is_word_started = 1;
    word[j] = 'A' + str[i] - 'a';
    j++;
    }
    else
    {
    word[j] = str[i];
    j++;
    }
    }
    else if(str[i] >= 'A' && str[i] <= 'Z')
    {
    if(is_word_started == 0)
    {
    is_word_started = 1;
    word[j] = str[i];
    j++;
    }
    else
    {
    word[j] = str[i];
    j++;
    }
    }
    else if(str[i] >= '0' && str[i] <= '9')
    {
    if(is_word_started == 0)
    {
    is_word_started = 1;
    word[j] = str[i];
    j++;
    }
    else
    {
    word[j] = str[i];
    j++;
    }
    }
    else
    {
    if(is_word_started == 1)
    {
    is_word_started = 0;
    word[j] = '\0';
    printf("%s\n", word);
    j = 0;
    }
    }

    }
    return 0;
    }

    উত্তরমুছুন
    উত্তরগুলি
    1. এই মন্তব্যটি লেখক দ্বারা সরানো হয়েছে।

      মুছুন
  4. ৯.৭ সমস্যাটা একটু ভিন্ন ভাবে করলাম

    #include
    int main()
    {
    char str1[] = "bangla";
    char str2[] = "desh";
    char str3[11];

    int i, j;
    for(i = 0, j = 0; str1[i] != 0; i++, j++)
    {
    str3[j] = str1[i];
    }
    for(i = 0; str2[i] != 0; i++,j++)
    {
    str3[j]= str2[i];
    }
    str3[j] = '\0';

    printf("%s", str3);

    return 0;
    }

    উত্তরমুছুন
  5. 9.8 প্রবলেমটা একটু মডিফাই করে সমাধান করলাম,
    প্রত্যেকটা শব্দ নতুন লাইন এ প্রিন্ট করতে হবে এবং শব্দের শেষ অক্ষরটা বড় হাতের হবে কিন্তু শেষ অক্ষরটা সংখ্যা হলে কোন পরিবরতন হবে না
    সমাধানঃ

    #include
    #include
    int main()
    {
    char str[1002], word[100];
    int i, j, length, is_word_started;

    gets(str);
    length = strlen(str);
    is_word_started = 0;
    printf("\n");


    for(i = 0, j= 0; i <= length; i++)
    {
    if(str[i] >= 'a' && str[i] <= 'z')
    {
    if(is_word_started == 0)
    {
    is_word_started = 1;
    }
    if(str[i+1] >= 'a' && str[i+1] <= 'z' || str[i+1] >= '0' && str[i+1] <= '9')
    {
    word[j] = str[i];
    j++;
    }
    else
    {
    word[j] = 'A' + (str[i] - 'a');
    j++;
    }
    }
    else if(str[i] >= 'A' && str[i] <= 'Z')
    {
    if(is_word_started == 0)
    {
    is_word_started = 1;
    }

    word[j] = str[i];
    j++;

    }
    else if(str[i] >= '0' && str[i] <= '9')
    {
    if(is_word_started == 0)
    {
    is_word_started = 1;
    }

    word[j] = str[i];
    j++;

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

    }
    return 0;
    }

    উত্তরমুছুন
  6. এই মন্তব্যটি লেখক দ্বারা সরানো হয়েছে।

    উত্তরমুছুন
  7. আমি ছি শিখতেছি সুবিন সার এর কম্পিউটার প্রোগ্রামিং বই দিয়ে আমি লুপ অ্যারে ফাংশন শেষ করে স্ট্রিং এর অধ্যায়ে আছি কিন্তু আমার মনে হয় আমি লুপ অ্যারে ভাল ভাবে বুঝতে পারতেছিনা বিশেষ করে স্ট্রিং এর অধ্যায়ে যে অ্যারেগুলি ব্যবহার করা হইছে। আমি বুঝতেছিনা আমি কি সামনে আগাব না আবার ভাল করে পরবো? আমি এই বইতে যে সমস্যা দেওয়া আছে বা কোড আছে সেগুলি বুঝতে পারতেছি কিন্তু নতুন কোন কিছু বুঝতে পারতেছিনা। আমার মনে হয় এটা অনেক টা ম্যাথ এর মত প্রাকটিছ করতে করতে ঠিক হয়ে যাবে সুবিন সার এর কাছে আমার প্রশ্ন আমার ধারনা টা কি ঠিক???? আমি কি সামনে এগিয়ে যাব??? আর আমি নাইন এ পরি আমি এইবার NHSPC তে অংশগ্রহণ করতে চাই। সেই জন্য সার এর কিছু পরামর্শ চাচ্ছি। কোথায় জানি পরছিলাম সার বলছিলেন যে, যখন কেউ প্রোগ্রামিং করতে করতে খাওয়ার কথা ভুলে যায় তখন তাকে তাকে প্রোগ্রামিং জগতে প্রবেশকারী বলয়া যায়। আমি অনেকটা এরকম হয়ে গেছি।

    উত্তরমুছুন

এখানে বিষয়সংশ্লিষ্ট মন্তব্য কিংবা প্রশ্ন করা যাবে। বাংলায় মন্তব্য করার সময় বাংলা হরফে লিখতে হবে। আর রোমান হরফে লিখলে ইংরেজিতে লিখতে হবে। নতুবা মন্তব্য প্রকাশ করা হবে না। ধন্যবাদ।