Learn C++ STL

lesson ১ / ৯ · string: যে text নিজের length নিজেই জানে

Module ৩ · string: যে text নিজের length নিজেই জানে

string: অক্ষরের একটা সারি, যে নিজের length নিজেই জানে

Freeপড়া

এই lesson-এ যা শিখবে

  • একটা string declare করে cin >> দিয়ে একটা শব্দ আর getline দিয়ে পুরো একটা লাইন পড়তে পারবে, তারপর ওর size print করতে পারবে।
  • Index দিয়ে একটা character পড়তে আর বদলাতে পারবে, + দিয়ে string জোড়া লাগাতে পারবে, আর == দিয়ে তুলনা করতে পারবে।
  • বাংলায় লেখা একটা নামের size() কেন চোখে দেখা অক্ষরের সংখ্যার সমান না, ওর byte-গুলো দেখিয়ে বুঝিয়ে বলতে পারবে।

Maria একটা C program-এ নিজের নাম টাইপ করছে, যেখানে আছে char name[10]। "Maria" দিব্যি এঁটে যায়। কিন্তু পুরো নাম "Maria Lopez" আঁটে না, বাড়তি অক্ষরগুলো গিয়ে পড়ে এমন memory-তে, যেটা array-র না। তার উপর প্রতিবার strlen অক্ষরগুলো আবার গুনে গুনে length বের করে। C++-এ এসবের কিছুই তোমার কাজ না: string name; নিজেই দরকারমতো বড় হয়, আর name.size() length আগে থেকেই জানে। এই lesson string দেখাবে, আর বাংলায় লেখা Maria-র নাম নিয়ে একটা সৎ চমকও।

সমস্যাটা: text যেটা এক জায়গায় থামে না

C track-এ text মানে ছিল '\0' দিয়ে শেষ হওয়া একটা char array। Program চলার আগেই ওর length ঠিক করে দিতে হতো। Copy করতে strcpy, জুড়তে strcat, মাপতে strlen, আর ওটা যতবার call করো, ততবার character একটা একটা করে গোনে। এর C++ উত্তর হলো string: character-এর এক সারি, যেটা নিজে নিজে বড় হয় আর নিজের length নিজেই মনে রাখে। ওর পুরো নাম std::string, আর ও থাকে <string> header-এ।

একই কাজ দুইবার করে দেখা যাক। একটা first name আর একটা last name পড়ো, মাঝে একটা space দিয়ে জোড়ো, তারপর ফলাফল আর ওর length print করো। আগে C-এর উপায়ে, যেটা C++ হিসেবেও compile হয়।

#include <cstdio>
#include <cstring>

int main() {
    char first[20], last[20], full[41];
    scanf("%19s %19s", first, last);
    strcpy(full, first);
    strcat(full, " ");
    strcat(full, last);
    printf("%s has %zu characters\n", full, strlen(full));
    return 0;
}
Maria Lopez has 11 characters

এবার C++-এর উপায়ে, একই input দিয়ে।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string first, last;
    cin >> first >> last;
    string full = first + " " + last;
    cout << full << " has " << full.size() << " characters\n";
    return 0;
}
Maria Lopez has 11 characters

এই output input Maria Lopez-এর জন্য। C version-এ লাগে তিনটা array-র size, overflow ঠেকাতে একটা %19s, আর জোড়া লাগাতে দুইটা library call। C++ version-এ কোথাও কোনো size নেই। মানে string হলো এমন text, যার size তোমাকে হাতে ঠিক করতে হয় না।

String হলো character-এর এক সারি, সাথে আরেকটু

ভেতরে একটা string ওর character-গুলো পাশাপাশি রাখে memory-র একটাই block-এ, ঠিক C array বা Module 2-এর vector-এর মতো। এখানে একটা character মানে একটা char, মানে এক byte। String আরও দুইটা সংখ্যা রাখে: ওর size, মানে এখন কয়টা character আছে, আর ওর capacity, মানে বড় block লাগার আগে কয়টা আঁটবে।

Block-এ আরও একটা জিনিস বসে থাকে। শেষ character-এর পরে library একটা '\0' রেখে দেয়, C string যে শেষ-চিহ্ন ব্যবহার করে, সেটাই। এটা size-এ গোনা হয় না। এটা থাকে যাতে string এক ধাপেই ওর text একটা C function-এর হাতে দিতে পারে (lesson 02 দেখাবে c_str())।

একটা string: ছোট একটা handle, ওর character-গুলো, আর তার পরের শেষ-চিহ্ন string name = "Maria"; name (handle) শুরু কোথায় size: 5 capacity: 15 size: 5টা character M a r i a \0 খালি জায়গা [0] [1] [2] [3] [4] [5] শেষ-চিহ্ন, size-এ নেই capacity: বড় block লাগার আগে 15টা character আঁটে size() জমা করা থাকে, তাই জিজ্ঞেস করতে কোনো খরচ নেই। C-এ strlen প্রতিবার গুনে বের করত।

মানে string হলো character-এর একটা vector, যেটা সাথে একটা শেষ-চিহ্নও রাখে। এজন্যই name.size() সাথে সাথে উত্তর দেয়, যেখানে strlen(name) পুরোটা হেঁটে আসত।

প্রথম দিনেই যে syntax লাগবে

একটা string, আর ওর রোজকার call-গুলো

#include <string>

string s;                     an empty string, ""
string s = "Maria";           a copy of this text
string s(5, '*');             5 copies of one character, "*****"
s.size()   s.length()         how many characters (bytes) it holds
s.empty()                     true when it holds none
s[i]                          the character at index i, 0 to s.size() - 1
s + t      s += t             a new joined string; add t to the end of s
s == t                        true when the two texts are the same
cin >> s;                     read one word, up to a space or a line break
getline(cin, s);              read a whole line, spaces included
  • string: type-টা। using namespace std; থাকলে লেখো string; না থাকলে std::string।
  • size() আর length(): একই সংখ্যার দুইটা নাম। Container-রা বলে size(), তাই এই track-ও তাই বলে।
  • s[i]: একটা char, যেটা পড়তেও পারো, বদলাতেও পারো। Vector-এর মতোই, i সীমার মধ্যে আছে কি না কেউ check করে না।
  • Double quote-এ "Maria" হলো text; single quote-এ 'M' হলো একটা char। String বানানো যায় দুটো থেকেই।

মানে string declare করো আর দশটা variable-এর মতোই, আর ওর উপর যা করবে, সেটা হয় একটা operator, নয়তো dot দিয়ে একটা call।

String বানানোর চার উপায়

String তুমি চার রকম চেহারায় বানাবে। এই program প্রতিটার একটা করে বানায়, আর bracket-এর মধ্যে print করে, যাতে খালিটাও চোখে পড়ে।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string empty_one;
    string name = "Maria";
    string stars(5, '*');
    string copy = name;
    copy[0] = 'm';

    cout << "[" << empty_one << "] size " << empty_one.size() << '\n';
    cout << "[" << name << "] size " << name.size() << '\n';
    cout << "[" << stars << "] size " << stars.size() << '\n';
    cout << "[" << copy << "] size " << copy.size() << '\n';
    return 0;
}
[] size 0
[Maria] size 5
[*****] size 5
[maria] size 5

নিচের table চার লাইনকে আবার পড়ে দেখায়।

Declarationকী পাওকখন ব্যবহার করবে
string s;কোনো character নেই, ""পরে এতে পড়বে বা যোগ করবে
string s = "text";text-টার একটা copyprogram লেখার সময়েই text জানো
string s(n, c);c character-এর n-টা copydash-এর একটা লাইন, তারার সারি, padding
string s = t;t string-এর একটা copyএকটা বদলাবে, অন্যটা রেখে দেবে

শেষ লাইনটা এমন একটা জিনিস দেখায়, যেটা C-এ = কখনো করত না। copy[0] বদলালেও name যেমন ছিল তেমনই থাকল। C-এ char* copy = name; লিখলে একই অক্ষরগুলোর একটা দ্বিতীয় নাম তৈরি হয়। মানে string-এ = text-টা copy করে; ভাগাভাগি করে না।

একটা শব্দ পড়া, আর একটা লাইন পড়া

cin >> s আগে সামনের space-গুলো বাদ দেয়, তারপর একটা শব্দ পড়ে: পরের space, tab বা line break পর্যন্ত character-গুলো। getline(cin, s) লাইনের শেষ পর্যন্ত সবকিছু পড়ে, space-সহ, আর line break-টা ফেলে দেয়। Module 1-এর lesson 02-তে দুটোরই দেখা পেয়েছ। এখানে দুটো একই লাইনের উপর।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string word, rest;
    cin >> word;
    getline(cin, rest);
    cout << "word: [" << word << "]\n";
    cout << "rest: [" << rest << "]\n";
    cout << "rest has " << rest.size() << " characters\n";
    return 0;
}
word: [Maria]
rest: [ Lopez likes tea]
rest has 16 characters

এই output input Maria Lopez likes tea-এর জন্য। >> থেমেছে প্রথম space-এ, আর space-টা input-এই রেখে গেছে। তারপর getline লাইনের বাকিটা নিয়েছে, ওই space দিয়েই শুরু করে। Module 1 যে ফাঁদ দেখিয়েছিল, এটাও সেটাই: cin >> n-এর পরে একটা getline পড়ে ফেলে সংখ্যার লাইনের খালি বাকি অংশটা। তাই শব্দ পড়ো >> দিয়ে, লাইন পড়ো getline দিয়ে, আর একটা থেকে অন্যটায় যাওয়ার আগে লাইনটা শেষ করে নাও।

Index, আর Bob-এর শেষ index

Character-গুলোর নম্বর শুরু হয় 0 থেকে, array-র বাক্সের মতো। Size n-এর একটা string-এর index 0 থেকে n - 1, আর s[i] হলো একটা char, যেটা পড়তে বা বদলাতে পারো। Bob "Maria"-র character code-গুলো print করছে, প্রতি index-এ একটা। শেষেরটা যেন বাদ না পড়ে, তাই ও লেখে <=।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string name = "Maria";
    for (size_t i = 0; i <= name.size(); i++) {
        cout << "[" << (int)name[i] << "]";
    }
    cout << '\n';
    return 0;
}
[77][97][114][105][97][0]

প্রথম পাঁচটা code হলো অক্ষরগুলো: ASCII table থেকে 77 মানে 'M', 97 মানে 'a'। ষষ্ঠটা, 0, হলো name[5], মানে name[name.size()]। Vector হলে এখানে ও block-এর বাইরে পড়ত, Module 2 যেমন দেখিয়েছে। String-এর ওই ঘরে বসে আছে শেষ-চিহ্নটা, আর standard বলে দিয়েছে s[s.size()] সেটাই পড়ে। তাই Bob-এর loop এমন একটা 0 print করে, যেটা ও চায়নি, কোনো crash নেই, কোনো message নেই।

আর এক ধাপ গেলেই আসল ঝামেলা। name[name.size() + 1] text-এর বাইরে, আর কেউ সেটা check করে না। Compiler Explorer-এ, Playground-এর flag দিয়ে, name[name.size() + 5] এক run-এ পড়েছে 9, পরের run-এ -15। ওগুলো পড়ে থাকা পুরোনো byte, তাই যেকোনো run যেকোনো কিছু দিতে পারে। তাই লেখো i < s.size(), আর মনে রাখো, s.size()-এর ঘরটা শেষ-চিহ্ন, কোনো character না।

size() vector-এর মতোই unsigned type, তাই খালি string-এ s.size() - 1 হলো 18446744073709551615, ঠিক যেমন Module 2-এর lesson 01 দেখিয়েছে। বিয়োগ করার আগে empty() check করে নাও।

String মানে byte: বাংলায় Maria-র নাম

Maria জানতে চায়, বাংলায় টাইপ করলে ওর নামের length অন্যরকম হয় কেন। উত্তরটা এই module-এর সবচেয়ে সৎ কথা: string রাখে byte, আর size() গোনে byte। একটা byte হলো 8 bit, 0 থেকে 255-এর মধ্যে একটা সংখ্যা। প্রতিটা ASCII character, মানে ইংরেজি অক্ষর, অঙ্ক আর যতিচিহ্ন, এক byte করে। বাংলা অক্ষর তা না।

Playground text রাখে UTF-8-এ, যে encoding প্রায় সব web page আর Linux system ব্যবহার করে। UTF-8 প্রতিটা ভাষার প্রতিটা character-কে একটা নম্বর দেয়, ওর code point, লেখা হয় U+09AE-এর মতো করে। তারপর ওই নম্বরটা লেখে এক থেকে চার byte দিয়ে। ASCII code point লাগে এক byte। প্রতিটা বাংলা code point লাগে তিন byte।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string en = "Maria";
    string bn = "মারিয়া";
    cout << en << ": size " << en.size() << '\n';
    cout << bn << ": size " << bn.size() << '\n';
    cout << "bytes:";
    for (unsigned char c : bn) {
        cout << ' ' << (int)c;
    }
    cout << '\n';
    return 0;
}
Maria: size 5
মারিয়া: size 21
bytes: 224 166 174 224 166 190 224 166 176 224 166 191 224 166 175 224 166 188 224 166 190

পাঁচ অক্ষর, size 5। বাংলা নামটা দেখতে তিন অক্ষর, মা, রি আর য়া, অথচ ওর size 21। আসলে এটা সাতটা code point: তিনটা ব্যঞ্জন, তিনটা কার আর একটা নুকতা, প্রত্যেকটা লেখা তিন byte দিয়ে। প্রতিটা byte 127-এর উপরে, তাই আলাদাভাবে কোনোটাই ASCII character না। Loop-এ unsigned char নেওয়া হয়েছে, যাতে byte-গুলো 0 থেকে 255 হিসেবে print হয়; Playground-এ সাধারণ char signed, ওটা দিলে এগুলো negative সংখ্যা হয়ে আসত। নিচে নামটা এক code point করে এগিয়ে দেখো।

বাংলায় Maria-র নাম, এক code point করে বানানো, প্রতি ধাপের পরে size()-সহ। মাপা হয়েছে GCC 12-এ, Playground-এর flag দিয়ে।

ধাপCode pointএটা কীওর byte-গুলোপরে size()
1U+09AEম, ব্যঞ্জন ম224 166 1743
2U+09BEা, আ-কার224 166 1906
3U+09B0র, ব্যঞ্জন র224 166 1769
4U+09BFি, ই-কার224 166 19112
5U+09AFয, ব্যঞ্জন য224 166 17515
6U+09BC়, নুকতা, যেটা য-কে য় বানায়224 166 18818
7U+09BEা, আ-কার224 166 19021

মানে একটা বাংলা শব্দকে তিনটা সংখ্যা দিয়ে বোঝানো যায়: size() যে byte গোনে, code point-এর সংখ্যা, আর পাঠক যে অক্ষর দেখে। ইংরেজি text-এ তিনটাই এক, এজন্যই এই module-এর problem-গুলোর input ASCII। বাংলায় s[0] হলো একটা অক্ষরের একটা byte, পুরো অক্ষর কখনো না।

+ দিয়ে জোড়া, == দিয়ে তুলনা

+ দুইটা string থেকে নতুন একটা string বানায়, আর += আগের একটা string-এর শেষে যোগ করে। == জিজ্ঞেস করে, দুইটা string-এ একই text আছে কি না। C-এ দুইটা char array-তে == দিলে তুলনা হতো ওদের address, তাই strcmp লাগত। এই program দুই আচরণ পাশাপাশি দেখায়।

#include <iostream>
#include <string>
using namespace std;

int main() {
    char a[] = "tea";
    char b[] = "tea";
    cout << "C arrays equal? " << (a == b) << '\n';

    string x = "tea";
    string y = "te";
    y += 'a';
    cout << "strings equal? " << (x == y) << '\n';

    string order = x + " and " + y;
    cout << order << '\n';
    return 0;
}
C arrays equal? 0
strings equal? 1
tea and tea

দুইটা array-তে একই অক্ষর, কিন্তু ওরা থাকে দুই address-এ, তাই a == b false। String-রা তুলনা করে character-গুলো, তাই y দুই ধাপে বানানো হলেও x == y true। += একটা পুরো string যত সহজে নেয়, একটা char, 'a', তত সহজেই নিয়েছে। মানে string-এ ==-এর অর্থ "একই text", আর তুমি এতদিন সেটাই বোঝাতে চাইছিলে।

+ নিয়ে একটা নিয়ম আছে: প্রতিটা জোড়ার অন্তত এক পাশে একটা string থাকতে হবে। x + " and " + y চলে, কারণ ওটা শুরু হয় একটা string থেকে, আর প্রতিটা ধাপ আবার একটা string ফেরত দেয়। Quote-এর ভেতরের দুইটা text string না, ওরা C array, তাই "Maria" + " " + last কোনো command line-এই compile হয় না। GCC 12 বলে error: invalid operands of types 'const char [6]' and 'const char [2]' to binary 'operator+'। একটা string দিয়ে শুরু করো, নয়তো লেখো string("Maria")।

Example 1: সবচেয়ে ছোট string program

একটা শব্দ পড়ো, তারপর print করো শব্দটা, ওর size, আর ওর প্রথম ও শেষ character।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string dish;
    cin >> dish;
    cout << dish << " has " << dish.size() << " letters\n";
    cout << "first " << dish[0] << ", last " << dish[dish.size() - 1] << '\n';
    return 0;
}
biryani has 7 letters
first b, last i

এই output input biryani-এর জন্য। dish.size() - 1 এখানে নিরাপদ শুধু এজন্য যে input-এ একটা শব্দ আছে। Input খালি হলে cin >> fail করে, dish খালিই থাকে, আর শেষ index ঘুরে বিশাল একটা সংখ্যা হয়ে যায়। সত্যিকারের user-এর জন্য লেখা program আগে dish.empty() check করে।

Run in Compiler
Example 2: Amara-র name card, লাইন ধরে ধরে

Amara পুরো নাম পড়ছে, প্রতি লাইনে একটা, input শেষ না হওয়া পর্যন্ত। প্রতিটার জন্য ও নাম আর ওর length print করে, আর শেষে সবচেয়ে লম্বা নামটা। নামে space আছে, তাই ও লাইন পড়ে, শব্দ না।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string line, longest;
    while (getline(cin, line)) {
        cout << line << " (" << line.size() << ")\n";
        if (line.size() > longest.size()) {
            longest = line;
        }
    }
    cout << "longest: " << longest << '\n';
    return 0;
}
Maria Lopez (11)
Bob (3)
Kenji Watanabe (14)
longest: Kenji Watanabe

এই output তিনটা লাইনের জন্য: Maria Lopez, Bob আর Kenji Watanabe। while (getline(cin, line)) input শেষ হলে থামে, যেমন থামত while (cin >> x)। longest = line; text-টা copy করে, তাই পরের getline ওটা বদলাতে পারে না।

Run in Compiler
Example 3: Zara-র password check

Zara একটা password check করে, ঠিক যেভাবে একটা sign-up form করে। কমপক্ষে 8টা character থাকতে হবে, আর থাকতে হবে একটা অঙ্ক আর একটা capital letter। বরাবরের মতো ও আগে খালি লাইনটা test করে, তাই program আলাদা কোনো ব্যবস্থা ছাড়াই ওটা সামলায়।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string pw;
    while (getline(cin, pw)) {
        bool digit = false, capital = false;
        for (char c : pw) {
            if (c >= '0' && c <= '9') {
                digit = true;
            }
            if (c >= 'A' && c <= 'Z') {
                capital = true;
            }
        }
        bool ok = pw.size() >= 8 && digit && capital;
        cout << "[" << pw << "] " << (ok ? "strong" : "weak") << '\n';
    }
    return 0;
}
[tea4two] weak
[Tea4two!] strong
[] weak
[Biryani2026] strong

এই output চারটা লাইনের জন্য, তৃতীয়টা খালি। Range-for string-এর প্রতিটা char-এর উপর দিয়ে হাঁটে, যেভাবে vector-এর element-এর উপর দিয়ে হাঁটত, আর খালি string-এ হাঁটার কিছুই থাকে না। Check-গুলো character-কে ASCII-র সীমার সাথে তুলনা করে, C track-এর অভ্যাস। একটা বাংলা অক্ষর 127-এর উপরের তিনটা byte, তাই ওটা অঙ্কও না, capital-ও না।

Run in Compiler

এটা কোথায় কাজে লাগে

  • Protocol Buffers. Google-এর এই message format একটা .proto file-এর প্রতিটা string field-কে generate করা C++ code-এ বানিয়ে দেয় একটা std::string, যেটা পড়া হয় name()-এর মতো একটা accessor দিয়ে। একটা field-এ যেকোনো length-এর text আসতে পারে, তাই generate করা code কখনো size বেছে নেয় না।
  • Chromium. Browser-এর URL class GURL পুরো address-টা রাখে একটা std::string-এ, আর দেয় spec() দিয়ে। একটা URL কত লম্বা, সেটা জানা যায় কেবল যখন কেউ টাইপ করে বা click করে।
  • nlohmann/json. C++-এর এই বহুল ব্যবহৃত JSON library default-ভাবে প্রতিটা JSON string মান রাখে একটা std::string-এ, ওর string_t type-এ। প্রতিটা মানের length ও জানতে পারে কেবল text পড়তে পড়তে।

যে ভুলগুলো সবাই করে

১. পুরো নাম cin >> দিয়ে পড়া।

string name;
cin >> name;
cout << "Hello, " << name << "!\n";

কোনো message নেই। Input Maria Lopez হলে এটা print করে Hello, Maria!। >> একটা শব্দ পড়ে, আর "Lopez" পরের read-এর জন্য input-এ বসে থাকে। যেখানে space থাকতে পারে, সেখানে getline(cin, name) ব্যবহার করো। এই ভুলটা তুমি করবে, কারণ এতদিন যত সংখ্যা পড়েছ, সবই >> দিয়ে দিব্যি চলেছে।

২. printf-এর %s-এ একটা string দিয়ে দেওয়া।

string name = "Maria";
printf("%s\n", name);

Playground-এর flag দিয়ে এটা কোনো message ছাড়াই compile হয়, আর Compiler Explorer-এ দুই run দুই রকম আবর্জনা character print করেছে। %s চায় একটা C string-এর address, আর string একদম অন্য জিনিস। নিজের machine-এ -Wall -Wextra দিলে GCC 12 সেটা বলে দেয়: warning: format '%s' expects argument of type 'char*', but argument 2 has type 'std::string' {aka 'std::__cxx11::basic_string<char>'} [-Wformat=]। Print করো cout দিয়ে, নয়তো দাও name.c_str() (lesson 02)। এটা করবে, কারণ printf হলো C track-এর অভ্যাস।

৩. এমন index-এ লেখা, যেটা এখনো নেই।

string s;
s[0] = 'M';
cout << "[" << s << "] size " << s.size() << '\n';

কোনো message নেই, আর Compiler Explorer-এ এক run print করেছে [] size 0। লেখাটা গিয়ে পড়েছে খালি string-এর শেষ-চিহ্নের উপর, যেটা undefined behaviour, আর size একটুও বদলায়নি। [] কখনো character যোগ করে না। লেখো s += 'M'; বা s.push_back('M');, নয়তো আগে জায়গা বানাও string s(1, ' '); দিয়ে। এটা করবে, কারণ ঠিক size-এর array-তে বাক্সগুলো আগে থেকেই থাকত।

মাথা খাটাও

Kenji চায় তিনটা a দিয়ে একটা string। Bob চায় "31" text-টা। ওরা এই দুই লাইন লেখে।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string kenji(3, 'a');
    string bob = "3" + 1;
    cout << "[" << kenji << "] [" << bob << "]\n";
    return 0;
}

দুই লাইনই কোনো message ছাড়া compile হয়। Program কী print করে, আর ওদের মধ্যে কে যা চেয়েছিল তা পেল?

"3" কি একটা string? "+ দিয়ে জোড়া, == দিয়ে তুলনা" অংশে দেখো + কী কী জোড়ে, আর মনে করো C-এ "3" জিনিসটা কী ছিল।

অনুশীলন ১সহজ

Kenji একটা শব্দের খেলা বানাচ্ছে, আর ওর দরকার একটা তালিকার সবচেয়ে লম্বা শব্দটা। Input শেষ না হওয়া পর্যন্ত শব্দ পড়ো। সবচেয়ে লম্বা শব্দ আর ওর length print করো। কয়েকটা শব্দের length সমান হয়ে সবচেয়ে বেশি হলে, ওদের প্রথমটা print করো।

Input. Space বা নতুন লাইন দিয়ে আলাদা word, input শেষ না হওয়া পর্যন্ত। শুরুতে কোনো count নেই।

Output. এক লাইনে: সবচেয়ে লম্বা word, একটা space, আর ওর length। সমান হলে, সবচেয়ে লম্বাগুলোর মধ্যে প্রথমটা।

Constraints. 1 থেকে 100000টা word, প্রতিটায় 1 থেকে 100টা ASCII character; পুরো input বড়জোর 1000000 byte।

Sample. Input Bob packs maps, snacks and a camera for the trip দিলে snacks 6।

#include <iostream>
#include <string>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    string word;
    while (cin >> word) {
        // Keep the longest word seen so far.
        // Replace it only when this word is strictly longer.
    }

    // Print the longest word, a space, and its length.

    return 0;
}

longest-word নামে গ্রেড হয়, এই module-এর problem set-এর একটা free problem। Hidden test-এ আছে একটামাত্র শব্দ, সমান length-এর অনেকগুলো শব্দ, আর 100000টা শব্দ। প্রথমটার বদলে শেষ লম্বা শব্দটা রেখে দেওয়া program ওরা ধরে ফেলে।

Run in Compiler
অনুশীলন ২মাঝারি

David name badge print করে। একটা পুরো নাম লাইন হিসেবে পড়ো। ওটাকে তারার একটা ফ্রেমের ভেতরে print করো: তারার একটা লাইন, তারপর নামের আগে * আর পরে * দিয়ে নামটা, তারপর আবার তারার লাইন। ফ্রেম ঠিক মাঝের লাইনটার সমান চওড়া।

Input. এক লাইন, নামটা, যাতে space থাকতে পারে। শুধু ASCII।

Output. তিন লাইন: তারা, ফ্রেমে বসানো নাম, তারা।

Constraints. নামে 1 থেকে 100টা character।

Sample. Input Maria Lopez দিলে আসে ***************, তারপর * Maria Lopez *, তারপর ***************। মাঝের লাইনটা 11 + 4 = 15 character।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string name;
    getline(cin, name);

    // Make a string of stars as long as the framed line,
    // then print the three lines.

    return 0;
}

আলাদা করে গ্রেড হয় না। Hint: string(n, '*') বানায় n-টা তারা, আর ফ্রেমে বসানো লাইনটা নামের size-এর চেয়ে চার বেশি।

Run in Compiler
অনুশীলন ৩কঠিন

Maria এমন একটা program চায়, যেটা byte না গুনে code point গোনে, যাতে "মারিয়া" দেয় 7 আর "Maria" দেয় 5। UTF-8 text-এর একটা লাইন পড়ো, আর ওতে কয়টা code point আছে print করো।

Input. UTF-8 text-এর এক লাইন, ইংরেজি, বাংলা বা দুটোই।

Output. একটা সংখ্যা, code point-এর গুনতি।

Constraints. লাইনে 1 থেকে 100000 byte।

Sample. Input Maria মারিয়া দিলে আসে 13: পাঁচটা অক্ষর, একটা space আর সাতটা code point।

#include <iostream>
#include <string>
using namespace std;

int main() {
    string line;
    getline(cin, line);

    // Count the bytes that start a code point.
    // Hint: look at the bytes of the Bangla name in this lesson.

    return 0;
}

আলাদা করে গ্রেড হয় না। Lesson যে কথাটা শুধু ইঙ্গিতে বলেছে: UTF-8-এ একটা বাংলা code point-এর দ্বিতীয় আর তৃতীয় byte সবসময় 128 থেকে 191-এর মধ্যে থাকে। যে byte দিয়ে একটা code point শুরু হয়, সেটা কখনো ওখানে থাকে না। বাকি byte-গুলো গোনো, unsigned char দিয়ে।

Run in Compiler

সচরাচর যে প্রশ্নগুলো আসে

  • size() লিখব, না length()?

    দুটো একই সংখ্যা দেয়, আর কোনোটারই খরচ নেই। Text-এর জন্য length() শুনতে ঠিক লাগে, কিন্তু STL-এর প্রতিটা container-এ আছে size(), তাই size() লিখলে সব container-এ code একইরকম পড়া যায়। এই track size() লেখে।

  • char array কি আর লাগবে?

    কদাচিৎ। কোনো C library function হয়তো const char* চাইবে, আর lesson 02 দেখাবে c_str(), যেটা ঠিক সেটাই দেয়। কোন অল্প কয়েকটা জায়গায় string-এর চেয়ে অন্য কিছু ভালো, lesson 04 তার তালিকা দেয়।

  • String কি আসলে একটা vector<char>?

    ভেতরে প্রায় একই রকম দেখায়: এক block-এ character, একটা size আর একটা capacity। উপরে ও যোগ করে text-এর যা লাগে: শেষ-চিহ্ন, জোড়ার জন্য +, পড়ার জন্য getline, আর খোঁজা ও কাটার জন্য find আর substr, যেগুলো lesson 02-এর তালিকায় আছে।

  • আমার বাংলা নাম আরেকটা computer-এ অন্য size দেখাল। কেন?

    একই অক্ষর একাধিক উপায়ে রাখা যায়। য় হতে পারে একটা code point, U+09DF, আবার হতে পারে দুইটা, য-এর পরে নুকতা, যেমন এই lesson-এ। U+09DF দিয়ে লিখলে নামটা 21 না, 18 byte। Screen-এ দুই ক্ষেত্রেই একই অক্ষর দেখায়, আর lesson 05 (Pro) দুটোই মেপে দেখায়।

মূল কথা

  • String হলো character-এর এক সারি, যেটা নিজে বড় হয় আর নিজের size মনে রাখে, তাই length বেছে নিতে বা strlen call করতে হয় না।
  • cin >> s পড়ে একটা শব্দ; getline(cin, s) পড়ে পুরো লাইন, space-সহ।
  • Index চলে 0 থেকে s.size() - 1; s[s.size()] হলো শেষ-চিহ্ন '\0', আর তার পরে কেউ কিছু check করে না।
  • + আর += জোড়া লাগায়, = text copy করে, আর == তুলনা করে text, address না।
  • size() গোনে byte: একটা ASCII character এক byte, একটা বাংলা code point তিন byte, তাই "মারিয়া"-র size 21।
  • আরও গভীরে যেতে চাইলে: Under the Hood, ছোট string-এর buffer আর + এর খরচ (Pro)।

এরপর Bob s = s + c দিয়ে একটা লম্বা লাইন বানায়, আর বসে বসে অপেক্ষা করে। কেন, সেটা দেখাতে lesson 02 string-এর প্রতিটা operation-এর খরচ মেপে দেখবে।

lesson ১ শেষ

শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।

পরেরটা: string-এর প্রতিটা operation, একটা একটা করে, খরচসহ

string: অক্ষরের একটা সারি, যে নিজের length নিজেই জানে | Learn C++ STL | Progsity