Module ১০ · String
Character, ASCII আর কাজের মধ্যে char type
এই lesson-এ যা শিখবে
char-কে একটা ছোট সংখ্যা হিসেবে দেখতে পারবে:%cআর%dদিয়ে print করতে পারবে, আর একটা digit বা অক্ষর থেকে তার মান বের করে আবার character-এ ফিরতে পারবে।- হাতে লেখা range-এর বদলে
ctype.hদিয়ে character যাচাই করতে আর বদলাতে পারবে, আর এর function-গুলো কেনintনেয়, বুঝিয়ে বলতে পারবে। - একটা লাইন এক character করে পড়তে পারবে, z পার হলে ঘুরিয়ে a-তে এনে অক্ষর সরাতে পারবে, আর Playground-এ 127-এর উপরের byte কী করে, বুঝিয়ে বলতে পারবে।
David একটা বাড়ির নম্বর এক character করে পড়ে, তাই 7-টা আসে একটা character হয়ে। ও পরের বাড়িটা চায়, তাই %d দিয়ে c + 1 print করে, আর screen-এ আসে 56। ওর program ভুল না: ও যে প্রশ্নটা করতে চায়নি, program ঠিক সেটারই উত্তর দিয়েছে। Character আসলে কী, আর কোন library দিয়ে ওকে নিরাপদে সামলানো যায়, এই lesson সেটাই দেখাবে।
Character আসলে একটা ছোট সংখ্যা
এই হলো David-এর program, শুধু যে লাইনটা ওকে অবাক করেছে, সেটুকু রেখে।
#include <stdio.h>
int main(void)
{
char c = '7';
printf("%d\n", c + 1);
printf("%c\n", c + 1);
return 0;
}
56
8
Character মানে একটা অক্ষর, digit, space বা চিহ্ন। C এটাকে রাখে একটা সংখ্যা হিসেবে, যার নাম character code, আর সংখ্যাটা আসে Module 1-এর ASCII table থেকে। '7'-এর code 55, তাই '7' + 1 হয় 56।
Module 4 lesson 6 দেখিয়েছিল, যোগের মধ্যে ঢুকলে একটা char আগে int হয়ে যায়। তাই c + 1 হলো int 56। %d সেটা সংখ্যা হিসেবে লেখে, আর %c লেখে সেই character, যার code 56, মানে 8। David চেয়েছিল পরের সংখ্যার মান, কিন্তু চেয়ে বসেছে পরের character-এর code।
তাই char রাখে একটা code, আর %c না %d, সেটা শুধু ঠিক করে code-টা কীভাবে দেখানো হবে।
Digit আর অক্ষরগুলো টানা সারিতে বসে
দশটা digit হলো code 48 থেকে 57, ক্রম মেনে, মাঝে কোনো ফাঁক নেই। বড় হাতের অক্ষর 65 থেকে 90, ছোট হাতের অক্ষর 97 থেকে 122, প্রতিটা সারি টানা। এখান থেকে তিনটা কৌশল আসে, আর তিনটাই সাধারণ যোগ-বিয়োগ।
c - '0'একটা digit character-কে তার মানে বদলে দেয়: 55 - 48 হলো 7। উল্টো দিকে, 0 থেকে 9-এর কোনো মানের সাথে'0'যোগ করলে আবার character ফিরে আসে।c - 'A'হলো A থেকে একটা বড় হাতের অক্ষরের দূরত্ব, তাইc - 'A' + 1হলো বর্ণমালায় তার ক্রম।'a' - 'A'হলো 32: প্রতিটা বড় হাতের অক্ষর থেকে তার ছোট হাতের অক্ষরের দূরত্ব।
#include <stdio.h>
int main(void)
{
char digit = '7';
int value = digit - '0';
char next = value + 1 + '0';
char letter = 'D';
printf("%c has code %d and value %d\n", digit, digit, value);
printf("value + 1 = %d, stored back as the character %c\n", value + 1, next);
printf("%c is letter %d of the alphabet\n", letter, letter - 'A' + 1);
printf("two letters on from %c: %c\n", letter, letter + 2);
printf("'a' - 'A' = %d, so %c + 32 is %c\n", 'a' - 'A', letter, letter + 32);
return 0;
}
7 has code 55 and value 7
value + 1 = 8, stored back as the character 8
D is letter 4 of the alphabet
two letters on from D: F
'a' - 'A' = 32, so D + 32 is d
C17 (section 5.2.1) টানা সারির কথা দেয় শুধু digit-এর বেলায়। IBM mainframe-এ এখনো EBCDIC নামে একটা character set চলে; সেখানে i হলো 137, j হলো 145, আর মাঝের code-গুলো কোনো অক্ষরই না। Playground চলে ASCII-তে, তাই এখানে অক্ষরের কৌশলগুলো খাটে।
32টা করে সারিতে সাজালে প্রতিটা ছোট হাতের অক্ষর বসে তার বড় হাতেরটার ঠিক এক সারি, মানে ঠিক 32 code, নিচে। আর Z আর a-এর মাঝে বসে ছয়টা চিহ্ন।
তাই digit-এর মান c - '0', আর ছোট-বড় হাতের দূরত্ব 'a' - 'A'।
ctype.h: character-টা কী, library-কে জিজ্ঞেস করো
অক্ষর কি না, check করতে Bob লেখে c >= 'a' && c <= 'z', আর বড় হাতের অক্ষরগুলো বাদ পড়ে যায়। ওর দ্বিতীয় চেষ্টা, c >= 'A' && c <= 'z', ওই ছয়টা চিহ্নকেও ঢুকতে দেয়। দুইটাই চুপচাপ compile হয়, আর ঠিক এই দুই ভুলের জন্যই ctype.h আছে।
Library function হলো এমন function, যেটা C-এর সাথেই আসে, আগে থেকে লেখা আর যাচাই করা, যেমন printf। <ctype.h> header একটা একটা character-এর জন্য এমন একগুচ্ছ function declare করে।
এই module যে ctype.h function-গুলো ব্যবহার করে
#include <ctype.h>
isalpha(c) isdigit(c) isspace(c) isupper(c) islower(c)
toupper(c) tolower(c)
isalpha: অক্ষর কি?isdigit:'0'থেকে'9'-এর কোনোটা কি?isspace: whitespace কি?isupper: বড় হাতের অক্ষর কি?islower: ছোট হাতের অক্ষর কি?- এই পাঁচটা না হলে return করে 0, আর হ্যাঁ হলে 0 ছাড়া অন্য কোনো সংখ্যা।
toupperআরtolowerবদলানো character-টা return করে, আর বদলানোর কিছু না থাকলেc-টাই ফেরত দেয়।cএকটাint: এমন একটা মান, যেটা একটাunsigned char-এ ধরে, নয়তোEOF।
এবার 128টা code-এর প্রত্যেকটার উত্তর library নিজেই দিক।
#include <stdio.h>
#include <ctype.h>
#define CODES 128
int main(void)
{
printf("isspace is true for codes:");
for (int code = 0; code < CODES; code++) {
if (isspace(code)) {
printf(" %d", code);
}
}
printf("\n");
printf("from 'A' to 'z' but not letters:");
for (int code = 'A'; code <= 'z'; code++) {
if (!isalpha(code)) {
printf(" %c", code);
}
}
printf("\n");
return 0;
}
isspace is true for codes: 9 10 11 12 13 32
from 'A' to 'z' but not letters: [ \ ] ^ _ `
Whitespace মানে যে character-গুলো print হলে ফাঁকা জায়গার মতো দেখায়। isspace এমন ছয়টা চেনে: tab, newline, vertical tab, form feed, carriage return আর space। হাতে লেখা test সাধারণত মনে রাখে দুইটা। দ্বিতীয় লাইনটা হলো Bob-এর দ্বিতীয় চেষ্টা, হাতেনাতে ধরা।
তাই library তিন দিক থেকে এগিয়ে। এটা প্রতিটা character-এর জন্য ঠিক, isdigit(c) পড়লেই বোঝা যায় কী জিজ্ঞেস করা হচ্ছে, আর যেখানে অক্ষরগুলো টানা সারিতে নেই, সেখানেও এটা ঠিক থাকে।
এর হ্যাঁ মানে শুধু "0 না", তাই লেখো if (isdigit(c)), কখনো if (isdigit(c) == 1) না।
তাই character যাচাই মানে একটা ctype.h call, আর তার উত্তর হয় শূন্য, নয়তো শূন্য না।
toupper আর tolower নতুন একটা character ফেরত দেয়
Alice সব উত্তর table-এ দেখতে ভালোবাসে, তাই ও একবারে পাঁচটা character-এর কথা জিজ্ঞেস করে, সেগুলো একটা char array-তে রেখে।
#include <stdio.h>
#include <ctype.h>
#define SAMPLES 5
int main(void)
{
char samples[SAMPLES] = {'q', 'Q', '7', '?', ' '};
printf("char code isalpha isdigit toupper tolower\n");
for (int i = 0; i < SAMPLES; i++) {
char c = samples[i];
printf(" '%c' %4d %7d %7d '%c' '%c'\n",
c, c, isalpha(c) != 0, isdigit(c) != 0, toupper(c), tolower(c));
}
return 0;
}
char code isalpha isdigit toupper tolower
'q' 113 1 0 'Q' 'q'
'Q' 81 1 0 'Q' 'q'
'7' 55 0 1 '7' '7'
'?' 63 0 0 '?' '?'
' ' 32 0 0 ' ' ' '
isalpha(c) != 0 একটা তুলনা, তাই এটা print করে 1 বা 0 (Module 4 lesson 2)। toupper ছোট হাতের অক্ষরের বড় হাতের রূপটা return করে, আর বাকি সবকিছু যেমন ছিল তেমনই ফেরত দেয়; tolower করে ঠিক উল্টোটা। কোনোটাই c-কে বদলায় না: Module 7-এর সব function-এর মতো, এরা একটা নতুন মান return করে। তাই character বদলাতে চাইলে লেখো c = toupper(c);।
তাই toupper আর tolower অক্ষর বদলায়, বাকিগুলো ছুঁয়েও দেখে না, আর ফলটা তোমার হাতে ফেরত দেয়।
একটা লাইন, এক character করে পড়া
Zara-র password-এর নিয়ম: অন্তত একটা বড় হাতের অক্ষর থাকতেই হবে। ওর program লাইনটা পড়ে এক character করে, Module 6 lesson 3 যে loop দিয়ে একটা খারাপ লাইন ফেলে দিয়েছিল, সেটা দিয়েই। এবার অবশ্য প্রতিটা character কাজে লাগে।
একটা লাইন, এক character করে পড়ার গড়ন
char c = 0;
while (scanf("%c", &c) == 1 && c != '\n') {
/* use c */
}
"%c"ঠিক একটা character পড়ে, space হলেও, তাই এর আগে কোনো space বসে না।== 1loop থামায় যখন input ফুরিয়ে যায় আরscanfreturn করেEOF।c != '\n'loop থামায় newline-এ, মানে Enter চাপলে যে character-টা যোগ হয়।
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char c = 0;
int capitals = 0;
while (scanf("%c", &c) == 1 && c != '\n') {
if (isupper(c)) {
capitals++;
}
}
printf("capitals: %d\n", capitals);
if (capitals > 0) {
printf("rule met\n");
} else {
printf("add a capital letter\n");
}
return 0;
}
Input Lima 42X, তারপর Enter দিয়ে এটা trace করো, প্রতি character-এ এক সারি।
| Pass | c | তার code | isupper(c) | pass-এর পরে capitals |
|---|---|---|---|---|
| 1 | L | 76 | হ্যাঁ | 1 |
| 2 | i | 105 | না | 1 |
| 3 | m | 109 | না | 1 |
| 4 | a | 97 | না | 1 |
| 5 | একটা space | 32 | না | 1 |
| 6 | 4 | 52 | না | 1 |
| 7 | 2 | 50 | না | 1 |
| 8 | X | 88 | হ্যাঁ | 2 |
| 9 | '\n' | 10 | check হয় না: loop শেষ | 2 |
capitals: 2
rule met
ওই output-টা input Lima 42X, তারপর Enter-এর জন্য।
Module 3 lesson 4 %c-এর আগে একটা space বসিয়েছিল, পড়ে থাকা newline-টা পার হওয়ার জন্য। এখানে space-গুলোই data, আর newline হলো থামার সংকেত, তাই কোনো space না। == 1-টাও দরকারি, কারণ তুমি input যেভাবে টাইপ করো, Playground ঠিক সেভাবেই পাঠায়। Enter না চাপলে কোনো newline আসে না, কিন্তু scanf return করে EOF, আর loop তবুও থামে।
এই input ASCII। 127-এর উপরের byte-এর জন্য যে একটা বাড়তি ধাপ লাগে, সেটা আসছে এই lesson-এর শেষ অংশে।
তাই loop প্রতি pass-এ একটা character পড়ে, আর থামে newline-এ, নয়তো input শেষ হলে।
Caesar shift: ঘুরে আসা অক্ষরের হিসাব
Caesar shift প্রতিটা অক্ষরকে বদলে দেয় বর্ণমালায় তার k ঘর পরের অক্ষর দিয়ে। রোমান লেখক Suetonius লিখে গেছেন, Julius Caesar k = 3 ব্যবহার করতেন। ঝামেলাটা বর্ণমালার শেষে: z-এর তিন ঘর পরে ঘুরে এসে c হওয়ার কথা।
ঘুরে আসার কাজটা করে Module 4-এর ভাগশেষের operator। প্রথমে অক্ষরটাকে 'a' থেকে তার দূরত্বে বদলাও, মানে 0 থেকে 25-এর একটা সংখ্যায়। তার সাথে k যোগ করো, তারপর 26 দিয়ে ভাগ করে ভাগশেষ নাও: এতে 28 ঘুরে হয়ে যায় 2। শেষে আবার 'a' যোগ করলে সেটা অক্ষর হয়ে যায়।
#include <stdio.h>
#define SHIFT 3
int main(void)
{
char c = 0;
while (scanf("%c", &c) == 1 && c != '\n') {
int position = c - 'a';
int moved = (position + SHIFT) % 26;
printf("%c", 'a' + moved);
}
printf("\n");
return 0;
}
Input lazy, তারপর Enter দিয়ে এটা trace করো।
| c | তার code | position | position + SHIFT | % 26-এর পরে moved | যা print হয় |
|---|---|---|---|---|---|
l | 108 | 11 | 14 | 14 | o |
a | 97 | 0 | 3 | 3 | d |
z | 122 | 25 | 28 | 2 | c |
y | 121 | 24 | 27 | 1 | b |
'\n' | 10 | loop শেষ, আর শেষের printf লাইনটা শেষ করে | |||
odcb
ওই output-টা input lazy, তারপর Enter-এর জন্য। Program ধরে নেয়, input-এ শুধু ছোট হাতের অক্ষর আছে; একটা বড় হাতের অক্ষর দিলে ভুল কিছু বের হবে। ছোট-বড় হাত ঠিক রাখা অনুশীলন 3-এর কাজ। আর decode করা, মানে পিছনের দিকে সরানো, তার নিজের একটা ফাঁদ আছে, সেটা পাবে problems lesson-এ।
তাই ঘুরে আসা মানে তিন ধাপ: দূরত্বে বদলাও, যোগ করে % 26 নাও, আবার অক্ষরে ফেরো।
127-এর উপরের byte: signed char আর একটা বাংলা অক্ষর
ASCII থামে 127-এ। অন্য লিপির লেখা সাধারণত রাখা হয় UTF-8-এ, যেখানে একটা character নেয় এক থেকে চার byte, আর একটা বাংলা অক্ষর নেয় তিন byte (Module 1 lesson 4)। একটা বাংলা অক্ষর এলে পড়ার loop কী দেখে, দেখো।
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char c = 0;
while (scanf("%c", &c) == 1 && c != '\n') {
unsigned char byte = (unsigned char)c;
printf("as char %4d as unsigned char %3d isalpha %d\n",
c, byte, isalpha(byte) != 0);
}
return 0;
}
as char 65 as unsigned char 65 isalpha 1
as char -32 as unsigned char 224 isalpha 0
as char -90 as unsigned char 166 isalpha 0
as char -123 as unsigned char 133 isalpha 0
ওই output-টা input Aঅ, তারপর Enter-এর জন্য, Compiler Explorer-এ x86-64 Linux-এর GCC 12-এ। একই input-এ Playground-ও একই চারটা code পড়েছে: 65, -32, -90 আর -123।
অ হলো তিনটা byte: 224, 166 আর 133। সাধারণ char signed হতে পারে, unsigned-ও হতে পারে (Module 2 lesson 4), আর Playground-এ এটা signed। তাই 224 বের হয় -32 হয়ে, ঠিক যে পথে Module 4 lesson 6-এর teaser-এ 200 হয়ে গিয়েছিল -56।
এতে ctype.h-এর নিয়ম ভাঙে: argument হতে হবে এমন মান, যেটা একটা unsigned char-এ ধরে, নয়তো EOF (C17 section 7.4)। তাই -32-এর উপর isalpha(c) হলো undefined behaviour। Playground-এর GNU C Library কপালগুণে এটা মেনে নেয়; অন্য library হয়তো crash করবে। সমাধান একটা cast: (unsigned char)c লিখলে -32 আবার 224 হয়ে যায়।
Cast-এর পরেও তিনটা byte-এর একটাও অক্ষর না: প্রতিটা একটা অক্ষরের তিন ভাগের এক ভাগ। সত্যিকারের বাংলা লেখা সামলাতে লাগে এমন library, যেটা UTF-8 বোঝে, যেমন ICU (International Components for Unicode)। Module 1 lesson 4 যে কথা দিয়েছিল, সেটা এভাবেই রাখা হলো।
এখন থেকে যে program এমন byte পড়ে, যা সে নিজে বেছে নেয়নি, সে cast-টা লেখে। ASCII লেখার বেলায়, যেমন এই module-এর test-গুলো, সাধারণ call-ই নিরাপদ।
তাই Playground-এ 127-এর উপরের byte একটা negative char, আর (unsigned char) সেটাকে ctype.h-এর জন্য নিরাপদ করে।
সবচেয়ে ছোট কাজের ctype.h program: একটা game জিজ্ঞেস করে save করবে কি না, আর player টাইপ করতে পারে y বা Y।
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char answer = 0;
int choice = 0;
scanf(" %c", &answer);
choice = tolower((unsigned char)answer);
if (choice == 'y') {
printf("game saved\n");
} else if (choice == 'n') {
printf("not saved\n");
} else {
printf("please type y or n\n");
}
return 0;
}
game saved
ওই output-টা input Y-এর জন্য। tolower দুই হাতকে এক করে দেয়, তাই ladder-এ চারটা না, দুইটা অক্ষর check করলেই চলে। এখানে " %c"-এর space-টা ঠিক আছে, কারণ এক অক্ষরের উত্তরের আগের space-গুলো নিছক বাড়তি।
David-এর প্রথম চাওয়াটা, এবার ঠিকভাবে: digit character-গুলো মিলে যে সংখ্যাটা লেখে, loop সেটা গড়ে তোলে।
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char c = 0;
int value = 0;
while (scanf("%c", &c) == 1 && isdigit((unsigned char)c)) {
value = value * 10 + (c - '0');
}
printf("%d + 1 = %d\n", value, value + 1);
return 0;
}
| c | তার code | c - '0' | pass-এর পরে value |
|---|---|---|---|
2 | 50 | 2 | 0 x 10 + 2 = 2 |
0 | 48 | 0 | 2 x 10 + 0 = 20 |
2 | 50 | 2 | 20 x 10 + 2 = 202 |
6 | 54 | 6 | 202 x 10 + 6 = 2026 |
'\n' | 10 | digit না: loop শেষ | 2026 |
2026 + 1 = 2027
ওই output-টা input 2026, তারপর Enter-এর জন্য। 10 দিয়ে গুণ করলে আগের digit-গুলো এক ঘর বাঁয়ে সরে যায়। scanf("%d") ভিতরে ঠিক এই পথেই হাঁটে, সাথে চিহ্ন (sign) আর overflow-এর check; এখানে দুইটার একটাও নেই, তাই input 9টা digit-এর মধ্যে রাখো।
Kenji-র maze game এক লাইন move পড়ে: w উপরে, s নিচে, a বাঁয়ে, d ডানে, ছোট বা বড় যে হাতেই হোক। অন্য যেকোনো character বাদ যায়, তবে গোনা হয়। নতুন কেউ ঠিক এই program-টাই লেখে, আর এটা ভালো program।
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char c = 0;
int x = 0;
int y = 0;
int ignored = 0;
while (scanf("%c", &c) == 1 && c != '\n') {
switch (tolower((unsigned char)c)) {
case 'w':
y++;
break;
case 's':
y--;
break;
case 'a':
x--;
break;
case 'd':
x++;
break;
default:
ignored++;
break;
}
}
printf("position (%d, %d), ignored %d\n", x, y, ignored);
return 0;
}
| c | tolower-এর পরে | Move | x | y | ignored |
|---|---|---|---|---|---|
W | w | উপরে | 0 | 1 | 0 |
d | d | ডানে | 1 | 1 | 0 |
D | d | ডানে | 2 | 1 | 0 |
s | s | নিচে | 2 | 0 | 0 |
a | a | বাঁয়ে | 1 | 0 | 0 |
? | ? | কিছু না | 1 | 0 | 1 |
w | w | উপরে | 1 | 1 | 1 |
'\n' | loop শেষ | 1 | 1 | 1 |
position (1, 1), ignored 1
ওই output-টা input WdDsa?w, তারপর Enter-এর জন্য। tolower আছে বলে switch-এ প্রতিটা move-এর জন্য দুইটা না, একটা case লাগে। default বাকি সব ধরে ফেলে, তাই টাইপে ভুল হলেও player নড়ে না।
এটা কোথায় কাজে লাগে
- Database। SQLite-এর SQL পড়ার অংশ
ctype.hব্যবহার করে না। কোন byte কী ধরনের, সেটা ঠিক করে নিজের 256 ঘরের table,sqlite3CtypeMapদিয়ে, আর প্রতিটা byte-কে আগেunsigned char-এ cast করে নেয়। Table নিজের বলে machine-এর ভাষার setting যা-ই হোক, উত্তর একই থাকে। - Password-এর নিয়ম। Linux-এর
pam_pwqualityনতুন password-এর digit, বড় হাতের অক্ষর, ছোট হাতের অক্ষর আর বাকি character গোনে। এরdcredit,ucredit,lcreditআরocreditsetting-গুলো ওই চার দলেরই নাম। - Cipher। ROT13 হলো k = 13-এর Caesar shift; Usenet-এ গল্পের শেষটা (spoiler) লুকাতে এটা চলত। দুইবার চালালে লেখাটা ফিরে আসে, আর Unix-এর এক command,
tr 'A-Za-z' 'N-ZA-Mn-za-m', এক লাইনেই কাজটা সারে। - Terminal। Ctrl-এর সাথে একটা অক্ষর চাপলে যায় বর্ণমালায় সেই অক্ষরের ক্রম: Ctrl+C হলো 3, Ctrl+D হলো 4। লাইনের শুরুতে Ctrl+D পেলে Unix terminal সেটাকে input-এর শেষ ধরে, আর
scanfreturn করেEOF।
যে ভুলগুলো সবাই করে
১. হাতে লেখা range, যেটা বড় হাতের অক্ষর বাদ দেয়।
if (c >= 'a' && c <= 'z') {
letters++;
}
কোনো command line-এই কোনো বার্তা নেই। পড়ার loop-এর ভিতরে input Dhaka Lima দিলে আসে letters: 7, 9 না, কারণ D আর L কখনো গোনা হয় না। লেখো isalpha((unsigned char)c)। তুমি range-টা লিখবে, কারণ বর্ণমালা বললে তোমার চোখে ছোট হাতের অক্ষরই ভাসে।
২. একটা character-এর দুই পাশে double quote।
char c = "A";
Playground-ও এটা দেখায়, Compile output tab-এ: warning: initialization of 'char' from 'char *' makes integer from pointer without a cast [-Wint-conversion]। বাংলা page-এ tab-টার নাম বাংলায় লেখা থাকে, ভিতরের লেখা একই। -Wall আর -Wall -Wextra-ও একই কথা বলে, আর GCC 14 এটাকে error বানিয়ে দেয়। "A" হলো একটা string, character-এর একটা সারি, যেটা খুলবে lesson 02; char * হলো Module 11-এর লেখার ধরন। Compiler Explorer-এ একবার run করে print হয়েছে একটা অদৃশ্য character, code 4, A না। লেখো 'A'; তুমি double quote-এর দিকে হাত বাড়াবে, কারণ printf লেখা রাখে double quote-এর ভিতরে।
৩. toupper call করে উত্তরটা ফেলে দেওয়া।
char c = 'q';
toupper(c);
printf("%c\n", c);
কোনো command line-এই কোনো বার্তা নেই, আর print হয় q। toupper বড় হাতের অক্ষরটা return করে, আর কিছুই বদলায় না, তাই সেটা রেখে দাও: c = toupper((unsigned char)c);। তুমি ভাববে call-টাই c-কে বদলে দেবে, কারণ নামটা শুনতে একটা হুকুমের মতো।
৪. পড়ার loop-এর ভিতরে %c-এর আগে space।
while (scanf(" %c", &c) == 1 && c != '\n') {
if (c == ' ') {
spaces++;
}
read++;
}
কোনো command line-এই কোনো বার্তা নেই। Format-এর space-টা প্রতিটা space পার করে যায়, newline-ও। Input Go on, Enter, তারপর no দিলে এই loop print করেছে spaces: 0, characters read: 6। Space-টা সে দেখেইনি, আর পড়তে পড়তে দ্বিতীয় লাইনে ঢুকে গেছে। ওই space ছাড়া print হয়েছে spaces: 1, characters read: 5। তুমি space-টা বসাবে, কারণ Module 3 lesson 4 এটাকে সমাধান হিসেবে শিখিয়েছিল, আর একটা সংখ্যার পরে এটা সত্যিই সমাধান।
Zara দেখতে চায়, একটা password-এর লাইন কী কী দিয়ে তৈরি। লাইনের অক্ষর, digit, space আর বাকি character গোনো।
Input. Printable ASCII character-এর একটা লাইন, space থাকতে পারে।
Output. চার লাইন: letters N, digits N, spaces N আর others N। অক্ষর আর digit মানে isalpha আর isdigit যা বলে, space মানে ' ', আর newline গোনা হয় না।
Constraints. লাইনে 0 থেকে 100টা character, প্রতিটার code 32 থেকে 126।
Sample. Input Dhaka 2026, go! দিলে চার লাইনে letters 7, digits 4, spaces 2 আর others 2।
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char c = 0;
int letters = 0;
int digits = 0;
int spaces = 0;
int others = 0;
while (scanf("%c", &c) == 1 && c != '\n') {
/* Add 1 to the counter that c belongs to. */
}
printf("letters %d\n", letters);
printf("digits %d\n", digits);
printf("spaces %d\n", spaces);
printf("others %d\n", others);
return 0;
}
char-classes নামে গ্রেড হয়। Hidden test-এ আছে একটা ফাঁকা লাইন আর 100 character-এর একটা লাইন। বড় হাতের অক্ষর বাদ দেওয়া range, আর newline গুনে ফেলা, দুইটাই সেখানে ধরা পড়ে।
David একটা লাইন যাচাই করে তার digit-গুলো যোগ করে, প্রতিটা আলাদা করে: 12 মানে 1, তারপর 2।
Input. Printable ASCII character-এর একটা লাইন।
Output. লাইনের digit-গুলোর মানের যোগফল, কোনো digit না থাকলে 0।
Constraints. লাইনে 0 থেকে 100টা character, প্রতিটার code 32 থেকে 126।
Sample. Input Room 12, floor 3 দিলে 6।
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char c = 0;
int sum = 0;
while (scanf("%c", &c) == 1 && c != '\n') {
/* If c is a digit, add its value to sum. */
}
printf("%d\n", sum);
return 0;
}
আলাদা করে গ্রেড হয় না। একটা লাইনের উপর দিয়ে এই একই হাঁটা char-classes-ই গ্রেড করে।
David-এর ক্লাব নোট লেখে সংকেতে: প্রতিটা অক্ষর k ঘর সামনে সরে, z থেকে ঘুরে a-তে। বড় হাতের অক্ষর বড় হাতেরই থাকে, আর বাকি সব character যেমন আছে তেমনই থাকে।
Input. প্রথম লাইনে k, তারপর এক লাইন লেখা।
Output. লেখাটা, যেখানে প্রতিটা অক্ষর সরানো, আর বাকি সব আগের মতো।
Constraints. 0 <= k <= 25। লেখায় 0 থেকে 100টা character, প্রতিটার code 32 থেকে 126।
Sample. Input 3, তারপর পরের লাইনে Zoo 42! দিলে Crr 42!।
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int k = 0;
char c = 0;
scanf("%d", &k);
while (scanf("%c", &c) == 1 && c != '\n') {
/* the rest of the first line: nothing to do */
}
while (scanf("%c", &c) == 1 && c != '\n') {
/* Shift c forward by k places if it is a letter, keeping
its case, then print it. Print any other c unchanged. */
}
printf("\n");
return 0;
}
আলাদা করে গ্রেড হয় না। Problems lesson-এর caesar-decode একই shift গ্রেড করে, উল্টো দিকে, অনেকগুলো লাইনে।
সচরাচর যে প্রশ্নগুলো আসে
isdigit('7')কেন 1 না দিয়ে 2048 দেয়?কারণ হ্যাঁ মানে 0 ছাড়া যেকোনো সংখ্যা, Module 4 lesson 2 যেমন সাবধান করে দিয়েছিল। Playground-এর GNU C Library যে bit flag-টা check করেছে, সেটাই return করে: Compiler Explorer-এ একবার run করে
isdigit('7')-এর জন্য পাওয়া গেছে 2048,isalpha('q')-এর জন্য 1024, আরisspace(' ')-এর জন্য 8192। তাই ওখানেisdigit(c) == 1কখনো সত্যি হয় না।Character তো
char, তাহলে এই function-গুলোintনেয় কেন?যাতে এরা
EOF-ও নিতে পারে, যেটা কোনো character না। এক character পড়ার C-র আরেকটা উপায়,getchar(), একটাintreturn করে, যেটা হয় একটাunsigned char-এর মান, নয়তোEOF।ctype.hঠিক এই পরিসরটাই মেনে নেয়, আর একটা negativecharপড়ে যায় এর বাইরে।%cকি একটা বাংলা অক্ষর print করতে পারে?এক call-এ না:
%cprint করে একটা byte। তবে তিনটা byte পরপর print করো, UTF-8 পড়তে পারে এমন screen অক্ষরটা আবার এঁকে দেবে। Compiler Explorer-এ একবার run করে পড়ার loop দিয়ে অ পড়া হয়েছে, প্রতিটা byte%cদিয়ে আবার print হয়েছে, আর দেখা গেছে অ, সাথে 3 byte-এর হিসাব।
মূল কথা
- একটা
charএকটা code রাখে:%cসেটা এঁকে দেখায়,%dসংখ্যাটা print করে, আর হিসাব চলে সংখ্যাটার উপর। - ASCII-তে digit আর অক্ষরগুলো টানা সারিতে বসে, তাই
c - '0',c - 'A' + 1আর% 26দিয়ে shift, সবই সাধারণ হিসাব। - Character যাচাই করো
ctype.hদিয়ে, range দিয়ে না: এটা প্রতিটা character-এর জন্য ঠিক, আর এর হ্যাঁ মানে "0 না"। toupperআরtolowerনতুন character-টা return করে; সেটা রেখে দাও, নইলে কিছুই বদলায় না।while (scanf("%c", &c) == 1 && c != '\n')একটা লাইন এক character করে পড়ে, space-সহ।- Playground-এ 127-এর উপরের byte একটা negative
char:ctype.h-এর জন্য সেটাকেunsigned char-এ cast করো, আর বাংলা লেখা ছেড়ে দাও একটা library-র হাতে।
এরপর Bob, Maria-র নামটা ঠিক পাঁচটা বাক্সে রাখে, আর print করলে নামের শেষে Dhaka জুড়ে বসে। হারিয়ে যাওয়া শূন্যটা খুঁজে বের করবে lesson 02।
lesson ১ শেষ
শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।
পরেরটা: String: যে array শূন্য দিয়ে শেষ হয়