Bài 30.5: Bài tập thực hành ứng dụng chuỗi trong C++

Chào mừng trở lại với chuỗi bài học C++!

Hôm nay, chúng ta sẽ đi sâu vào một chủ đề cực kỳ quan trọngthực tế: làm việc với chuỗi (string) trong C++. Chuỗi không chỉ là tập hợp các ký tự; chúng là nền tảng để xử lý văn bản, giao tiếp với người dùng, phân tích dữ liệu... nói chung là không thể thiếu trong hầu hết các ứng dụng!

Trong bài viết này, chúng ta sẽ không chỉ học lý thuyết mà còn thực hành ngay lập tức với các ví dụ C++ ngắn gọndễ hiểu, tập trung vào các ứng dụng phổ biến nhất của string.

Hãy cùng bắt đầu nào!

1. Nhập Xuất Chuỗi: cin vs getline

Việc đầu tiên khi làm việc với chuỗi là làm sao để đưa dữ liệu vào và hiển thị chúng ra. Trong C++, chúng ta thường dùng cincout. Tuy nhiên, có một điểm khác biệt quan trọng khi nhập chuỗi:

  • cin >> myString;: Sẽ chỉ đọc một từ duy nhất, dừng lại khi gặp ký tự khoảng trắng (space, tab, newline).
  • getline(cin, myString);: Sẽ đọc toàn bộ dòng cho đến khi gặp ký tự newline.

Hãy xem ví dụ minh họa sự khác biệt này:

#include <iostream>
#include <string>
#include <limits> // Can thiet de su dung numeric_limits

int main() {
    string word, line;

    cout << "Nhap mot tu (vd: XinChao): ";
    cin >> word; // Chi doc den khoang trang dau tien
    cout << "Ban vua nhap tu: " << word << endl;

    // Can xoa bo dem ban phim sau khi dung cin >>
    // Neu khong, ky tu xuong dong con lai se bi getline doc ngay lap tuc
    cin.ignore(numeric_limits<streamsize>::max(), '\n');

    cout << "Nhap mot dong (vd: Xin chao the gioi): ";
    getline(cin, line); // Doc ca dong, bao gom ca khoang trang
    cout << "Ban vua nhap dong: " << line << endl;

    return 0;
}

Giải thích: Ví dụ này làm nổi bật sự khác biệt. Khi nhập "Xin chao", cin >> word chỉ đọc "Xin". Phần " chao" và ký tự newline vẫn còn trong bộ đệm nhập. Nếu không dùng cin.ignore(), lời gọi getline tiếp theo sẽ thấy ngay ký tự newline còn sót lại và kết thúc ngay lập tức mà không chờ bạn nhập gì cả. cin.ignore(...) giúp bỏ qua các ký tự còn lại trong bộ đệm nhập đến hết dòng.

2. Độ Dài Chuỗi và Truy Cập Ký Tự

Một trong những thao tác cơ bản nhất là kiểm tra độ dài của chuỗi và truy cập từng ký tự.

  • myString.length() hoặc myString.size(): Trả về số lượng ký tự trong chuỗi.
  • myString[index] hoặc myString.at(index): Truy cập ký tự tại vị trí index (bắt đầu từ 0). at() an toàn hơn vì nó kiểm tra chỉ số có hợp lệ không và ném ra ngoại lệ nếu không.
#include <iostream>
#include <string>

int main() {
    string example = "Lap trinh C++";

    // Lay do dai
    cout << "Chuoi: '" << example << "'" << endl;
    cout << "Do dai chuoi: " << example.length() << endl; // Hoac example.size()

    // Truy cap ky tu
    cout << "Ky tu dau tien: " << example[0] << endl; // Cach thong thuong
    cout << "Ky tu tai vi tri 4: " << example.at(4) << endl; // An toan hon

    // In tung ky tu mot
    cout << "Cac ky tu: ";
    for (int i = 0; i < example.length(); ++i) {
        cout << example[i] << " ";
    }
    cout << endl;

    return 0;
}

Giải thích: Đoạn code trên cho thấy cách lấy độ dài và truy cập các ký tự riêng lẻ bằng cả hai cách [].at(). Vòng lặp for minh họa cách duyệt qua toàn bộ chuỗi để xử lý từng ký tự một, một kỹ thuật rất phổ biến.

3. Nối Chuỗi (Concatenation)

Ghép hai hoặc nhiều chuỗi lại với nhau là một thao tác thường xuyên được sử dụng.

  • Sử dụng toán tử +.
  • Sử dụng phương thức append().
#include <iostream>
#include <string>

int main() {
    string part1 = "Xin chao";
    string part2 = " the gioi!";

    // Noi chuoi bang toan tu +
    string fullString = part1 + part2;
    cout << "Noi bang +: " << fullString << endl; // Tao ra chuoi moi

    // Noi chuoi bang append()
    string message = "Hom nay la ";
    message.append("thu Hai."); // Noi vao chuoi hien co
    cout << "Noi bang append(): " << message << endl;

    // Noi ky tu vao cuoi chuoi
    message += '!'; // Tuong duong voi message.push_back('!');
    cout << "Noi ky tu: " << message << endl;


    return 0;
}

Giải thích: Toán tử + tạo ra một chuỗi mới là kết quả nối của hai chuỗi, trong khi phương thức append() sẽ thêm nội dung vào cuối chuỗi hiện tại mà nó được gọi. Toán tử += cũng có thể được sử dụng để nối chuỗi hoặc một ký tự vào cuối chuỗi.

4. Tìm Kiếm trong Chuỗi

Tìm xem một ký tự hoặc một chuỗi con có tồn tại trong một chuỗi khác không, và nếu có thì ở vị trí nào.

  • myString.find(substring): Tìm kiếm sự xuất hiện đầu tiên của substring trong myString. Trả về vị trí tìm thấy (kiểu size_t). Nếu không tìm thấy, trả về string::npos.
#include <iostream>
#include <string>

int main() {
    string sentence = "Hoc lap trinh C++ that la thu vi.";
    string wordToFind = "C++";
    string nonExistentWord = "Java";

    // Tim kiem tu "C++"
    size_t pos = sentence.find(wordToFind);

    if (pos != string::npos) { // Kiem tra xem co tim thay khong
        cout << "Tim thay '" << wordToFind << "' tai vi tri: " << pos << endl;
        // Vi tri nay la index bat dau cua chuoi con tim thay
    } else {
        cout << "Khong tim thay '" << wordToFind << "' trong chuoi." << endl;
    }

    // Tim kiem tu "Java"
    pos = sentence.find(nonExistentWord);
    if (pos != string::npos) {
         cout << "Tim thay '" << nonExistentWord << "' tai vi tri: " << pos << endl;
    } else {
        cout << "Khong tim thay '" << nonExistentWord << "' trong chuoi." << endl;
    }

    // Tim ky tu
    size_t charPos = sentence.find('t');
     if (charPos != string::npos) {
         cout << "Ky tu 't' dau tien tai vi tri: " << charPos << endl;
    }


    return 0;
}

Giải thích: Phương thức find()công cụ mạnh mẽ để kiểm tra sự tồn tại và vị trí của một chuỗi con. Điều quan trọng nhất cần nhớ là kết quả trả về phải được so sánh với string::npos để biết liệu chuỗi con có được tìm thấy hay không. string::npos là một hằng số đặc biệt có giá trị biểu thị "không tìm thấy".

5. Trích Xuất Chuỗi Con (Substring)

Để lấy một phần (một đoạn) của chuỗi ban đầu, chúng ta sử dụng substr().

  • myString.substr(pos): Trả về chuỗi con từ vị trí pos đến hết chuỗi.
  • myString.substr(pos, len): Trả về chuỗi con bắt đầu từ vị trí pos và có độ dài len.
#include <iostream>
#include <string>

int main() {
    string data = "ID:12345-STATUS:Active-USER:Admin";

    // Lay ID (tu vi tri 3, do dai 5 ky tu)
    string id = data.substr(3, 5);
    cout << "ID: " << id << endl; // Output: 12345

    // Lay STATUS (tim vi tri "STATUS:", sau do lay tiep)
    size_t statusPos = data.find("STATUS:");
    if (statusPos != string::npos) {
        string status = data.substr(statusPos + 7, 6); // +7 de bo qua "STATUS:"
        cout << "STATUS: " << status << endl; // Output: Active
    }

    // Lay USER (tim vi tri "USER:", sau do lay den het)
     size_t userPos = data.find("USER:");
    if (userPos != string::npos) {
        string user = data.substr(userPos + 5); // +5 de bo qua "USER:", lay den het
        cout << "USER: " << user << endl; // Output: Admin
    }

    return 0;
}

Giải thích: substr()công cụ chính để "cắt" chuỗi. Ví dụ này minh họa cách sử dụng nó để phân tích một chuỗi dữ liệu đơn giản dựa trên các điểm mốc đã biết hoặc được tìm thấy bằng find(). Kết hợp find()substr() là một kỹ thuật phổ biến để phân tích cú pháp chuỗi.

6. Thay Thế trong Chuỗi

Thay đổi một phần của chuỗi bằng một chuỗi khác.

  • myString.replace(pos, len, replacementString): Xóa len ký tự từ vị trí pos và chèn replacementString vào vị trí đó.
#include <iostream>
#include <string>

int main() {
    string sentence = "Ban nen hoc Java de co viec lam.";

    // Tim vi tri cua "Java"
    size_t pos = sentence.find("Java");

    if (pos != string::npos) {
        // Thay the "Java" (4 ky tu) bang "C++" (3 ky tu) tai vi tri tim thay
        sentence.replace(pos, 4, "C++");
        cout << "Sau khi thay the: " << sentence << endl;
        // Output: Ban nen hoc C++ de co viec lam.
    } else {
        cout << "Khong tim thay tu can thay the." << endl;
    }

    return 0;
}

Giải thích: replace() là cách hiệu quả để sửa đổi trực tiếp một phần của chuỗi. Chúng ta thường cần dùng find() trước để xác định vị trí và độ dài của phần cần thay thế.

7. Chèn và Xóa Ký Tự/Chuỗi Con

Thêm hoặc bớt các ký tự/chuỗi con tại một vị trí bất kỳ.

  • myString.insert(pos, stringToInsert): Chèn stringToInsert vào vị trí pos.
  • myString.erase(pos, len): Xóa len ký tự bắt đầu từ vị trí pos. Nếu bỏ qua len, sẽ xóa đến hết chuỗi.
#include <iostream>
#include <string>

int main() {
    string baseString = "Day la chuoi goc.";

    // Chen chuoi vao giua
    baseString.insert(7, "mot "); // Chen "mot " vao vi tri 7
    cout << "Sau khi chen: " << baseString << endl;
    // Output: Day la mot chuoi goc.

    // Xoa mot phan
    baseString.erase(4, 3); // Xoa 3 ky tu " la" tai vi tri 4
    cout << "Sau khi xoa: " << baseString << endl;
    // Output: Day mot chuoi goc.

    // Xoa den het tu mot vi tri
    baseString.erase(4); // Xoa tu vi tri 4 den het
    cout << "Sau khi xoa tu vi tri 4 den het: " << baseString << endl;
    // Output: Day

    return 0;
}

Giải thích: insert()erase() cho phép bạn thao tác chi tiết cấu trúc của chuỗi, thêm hoặc bớt nội dung tại các điểm cụ thể.

8. Kiểm Tra Chuỗi Rỗng

Một thao tác kiểm tra đơn giản nhưng hữu ích là kiểm tra xem chuỗi có rỗng hay không.

  • myString.empty(): Trả về true nếu chuỗi không chứa ký tự nào (độ dài là 0), ngược lại trả về false.
#include <iostream>
#include <string>
#include <limits> // Can thiet cho numeric_limits

int main() {
    string str1 = ""; // Chuoi rong
    string str2 = "Hello"; // Chuoi khong rong
    string userInput;

    if (str1.empty()) {
        cout << "str1 la chuoi rong." << endl;
    } else {
        cout << "str1 khong rong." << endl;
    }

     if (str2.empty()) {
        cout << "str2 la chuoi rong." << endl;
    } else {
        cout << "str2 khong rong." << endl;
    }

    cout << "Nhap gi do (hoac enter ngay de bo trong): ";
    getline(cin, userInput);

    if (userInput.empty()) {
        cout << "Ban da nhap mot dong rong." << endl;
    } else {
        cout << "Ban da nhap dong: '" << userInput << "'" << endl;
    }

    return 0;
}

Giải thích: Phương thức empty() là cách chính xácrõ ràng nhất để kiểm tra xem một chuỗi có rỗng không, thay vì kiểm tra myString.length() == 0.

9. Chuyển Đổi giữa Chuỗi và Số

Trong các ứng dụng thực tế, chúng ta thường xuyên cần chuyển đổi dữ liệu nhập vào dạng chuỗi thành số để tính toán, hoặc ngược lại, chuyển số thành chuỗi để hiển thị hoặc ghép nối.

  • Chuỗi sang Số: C++11 trở lên cung cấp các hàm như stoi (string to int), stol (string to long), stod (string to double), v.v.
  • Số sang Chuỗi: C++11 trở lên cung cấp hàm to_string().
#include <iostream>
#include <string> // Can thiet cho stoi, stod, to_string
#include <limits> // Can thiet cho numeric_limits (neu can xu ly input)
#include <stdexcept> // Can thiet de bat exception cho stoi, stod

int main() {
    string strNum = "123";
    string strDouble = "3.14159";
    string invalidStr = "abc";
    int age = 25;
    double price = 99.99;

    // Chuyen tu chuoi sang so
    try { // Nen dung try-catch vi cac ham stoi/stod co the gay loi
        int num = stoi(strNum);
        double dbl = stod(strDouble);
        // int errorNum = stoi(invalidStr); // Dong nay se gay ra exception

        cout << "Chuoi '" << strNum << "' thanh so nguyen: " << num << endl;
        cout << "Chuoi '" << strDouble << "' thanh so thuc: " << dbl << endl;

    } catch (const invalid_argument& ia) {
        cerr << "Loi doi so khong hop le khi chuyen doi: " << ia.what() << '\n';
    } catch (const out_of_range& oor) {
        cerr << "Loi ngoai pham vi khi chuyen doi: " << oor.what() << '\n';
    }


    // Chuyen tu so sang chuoi
    string ageStr = to_string(age);
    string priceStr = to_string(price);

    cout << "So nguyen " << age << " thanh chuoi: '" << ageStr << "'" << endl;
    cout << "So thuc " << price << " thanh chuoi: '" << priceStr << "'" << endl;

    // Ket hop so (da thanh chuoi) voi chuoi khac
    cout << "Toi " + ageStr + " tuoi." << endl;


    return 0;
}

Giải thích: Các hàm stoi, stol, stod,... rất tiện lợi, nhưng bạn cần lưu ý rằng chúng có thể gây ra lỗi (ném ra exception) nếu chuỗi đầu vào không phải là một số hợp lệ hoặc số quá lớn/nhỏ so với kiểu dữ liệu đích. Việc sử dụng khối try-catch như trong ví dụ là cách tốt để xử lý các trường hợp này một cách an toàn. Ngược lại, to_string() là một hàm đơn giản và ít khi gây lỗi, giúp chuyển bất kỳ kiểu số cơ bản nào thành string.

Comments

There are no comments at the moment.