BÀI GIẢNG: XÂU KÝ TỰ (STRING) TRONG C++

1. Giới thiệu về Xâu ký tự trong C++

Trong C++, để xử lý chuỗi văn bản (tên, văn bản, chuỗi số...), chúng ta có hai cách chính:

  1. C-style string: Mảng các ký tự kiểu char kết thúc bằng ký tự null \0 (kế thừa từ ngôn ngữ C).
  2. std::string (Class String): Kiểu dữ liệu hướng đối tượng thuộc thư viện chuẩn của C++. Đây là cách tiếp cận hiện đại, an toàn và linh hoạt hơn rất nhiều.

Bài giảng này sẽ tập trung toàn bộ vào std::string.

Cú pháp khai báo thư viện:

C++
#include <iostream>
#include <string> // Thư viện bắt buộc để dùng string
using namespace std;


2. Các cách khởi tạo một Xâu ký tự

Có rất nhiều cách để khai báo và khởi tạo một đối tượng string:

C++
string s1;           // Khởi tạo xâu rỗng ("")
string s2 = "Hello"; // Khởi tạo với chuỗi literal
string s3("World");  // Khởi tạo giống như gọi constructor
string s4(s2);       // Sao chép từ xâu s2 (s4 = "Hello")
string s5(5, 'A');   // Tạo xâu gồm 5 ký tự 'A' -> "AAAAA"

3. Nhập/Xuất Xâu ký tự (Input/Output)

Việc nhập dữ liệu cho xâu ký tự trong C++ cần lưu ý hiện tượng trôi lệnh khi có khoảng trắng.

3.1. Dùng cin (Bị giới hạn bởi khoảng trắng)

cin sẽ dừng đọc khi gặp dấu cách (space), dấu tab hoặc dấu xuống dòng (\n).

C++
string s;
cout << "Nhập xâu: ";
cin >> s; // Nếu nhập "Tin Học", s chỉ nhận được "Tin"
cout << "Xâu vừa nhập: " << s << endl;

3.2. Dùng getline() (Đọc toàn bộ dòng, có cả khoảng trắng)

Để đọc một chuỗi có khoảng trắng, ta dùng hàm getline().

C++
string s;
cout << "Nhập họ và tên: ";
getline(cin, s); // Nhập "Nguyễn Văn A" -> s nhận toàn bộ "Nguyễn Văn A"
cout << "Xin chào: " << s << endl;

⚠️ Lưu ý sửa lỗi trôi lệnh (cin.ignore())

Nếu trước lệnh getline() có một lệnh cin >> (ví dụ nhập số tuổi, số lượng), ký tự xuống dòng \n vẫn còn sót lại trong bộ nhớ đệm. getline() sẽ đọc ký tự \n này và kết thúc ngay lập tức khiến xâu bị rỗng.

  • Giải pháp: Dùng cin.ignore() trước getline().
C++
int age;
string name;
cin >> age;
cin.ignore(); // Xóa ký tự '\n' còn sót lại
getline(cin, name);

4. Truy cập ký tự và Duyệt xâu

Mỗi ký tự trong xâu được đánh chỉ số (index) bắt đầu từ 0 đến n-1 (với n là độ dài xâu).

4.1. Truy cập qua chỉ số [] hoặc .at()

  • s[i]: Nhanh hơn, nhưng không kiểm tra nếu chỉ số i vượt quá phạm vi.
  • s.at(i): An toàn hơn, ném ra ngoại lệ nếu i hợp lệ.

4.2. Các cách duyệt xâu phổ biến

  • Cách 1: Duyệt bằng chỉ số (Index-based loop)
C++
string s = "Hello";
for (int i = 0; i < s.length(); i++) {
    cout << s[i] << " ";
} // Đầu ra: H e l l o
  • Cách 2: Duyệt qua từng ký tự (Range-based for loop - C++11)
C++
string s = "C++";
for (char c : s) {
    cout << c << "-";
} // Đầu ra: C-+-+-

5. Bảng tổng hợp các Hàm (Methods) thành viên của std::string

Dưới đây là hệ thống toàn bộ các hàm thông dụng được chia theo nhóm chức năng:

5.1. Nhóm hàm kiểm tra kích thước

Tên hàm Chức năng Ví dụ
s.length() hoặc s.size() Trả về độ dài (số ký tự) của xâu. "Hi".length() \(\rightarrow\) 2
s.empty() Kiểm tra xâu có rỗng không (trả về true/false). s.empty()
s.clear() Xóa toàn bộ nội dung xâu, biến xâu thành rỗng. s.clear();

5.2. Nhóm hàm chỉnh sửa xâu (Thêm, Xóa, Chèn, Thay thế)

Tên hàm Chức năng Ví dụ minh họa
s.push_back(ch) Thêm ký tự ch vào cuối xâu. s = "Abc"; s.push_back('D'); \(\rightarrow\) "AbcD"
s.pop_back() Xóa 1 ký tự ở cuối xâu (C++11). s = "Abc"; s.pop_back(); \(\rightarrow\) "Ab"
s.append(str) Nối xâu str vào cuối xâu s (tương đương toán tử +=). s.append("Def");
s.insert(pos, str) Chèn xâu str vào vị trí pos. "Hlo".insert(1, "el") \(\rightarrow\) "Hello"
s.erase(pos, len) Xóa len ký tự bắt đầu từ vị trí pos. "Hello".erase(1, 2) \(\rightarrow\) "Hlo"
s.replace(pos, len, str) Thay thế len ký tự từ vị trí pos bằng xâu str. "AxxB".replace(1, 2, "yy") \(\rightarrow\) "AyyB"

5.3. Nhóm hàm tìm kiếm và cắt xâu

Tên hàm Chức năng Chi tiết
s.substr(pos, len) Trích xuất một xâu con từ vị trí pos với độ dài len. Nếu bỏ qua len, hàm sẽ cắt đến hết xâu.
s.find(str) Tìm vị trí xuất hiện đầu tiên của xâu str trong s. Trả về chỉ số đầu tiên, nếu không thấy trả về string::npos.
s.rfind(str) Tìm vị trí xuất hiện cuối cùng của xâu str. Duyệt từ phải qua trái.

6. Các toán tử (Operators) trên Xâu

std::string hỗ trợ các toán tử trực quan giúp code ngắn gọn hơn:

  • Toán tử ++=: Dùng để ghép (nối) xâu.

    C++
    string a = "Tin", b = "Học";
    string c = a + " " + b; // c = "Tin Học"
    

  • Toán tử so sánh (==, !=, <, >, <=, >=): So sánh hai xâu theo thứ tự từ điển (Lexicographical order).

    C++
    string x = "Apple";
    string y = "Banana";
    if (x < y) cout << "Apple đứng trước Banana trong từ điển";
    


7. Các hàm bổ trợ quan trọng (Thuộc thư viện <cctype><algorithm>)

Để xử lý xâu nâng cao, ta thường kết hợp các hàm ngoài thư viện string.

7.1. Thư viện <cctype> (Xử lý từng ký tự)

  • toupper(c): Chuyển ký tự thành chữ hoa.
  • tolower(c): Chuyển ký tự thành chữ thường.
  • isalpha(c): Kiểm tra có phải chữ cái không.
  • isdigit(c): Kiểm tra có phải chữ số không.

7.2. Chuyển đổi Xâu \(\leftrightarrow\) Số (C++11)

  • Số sang Xâu: to_string(value)

    C++
    int n = 123;
    string s = to_string(n); // s = "123"
    

  • Xâu sang Số: stoi(s) (sang int), stoll(s) (sang long long), stod(s) (sang double).

    C++
    string s = "45.67";
    double d = stod(s); // d = 45.67
    

7.3. Đảo ngược xâu (Thư viện <algorithm>)

C++
string s = "abcdef";
reverse(s.begin(), s.end()); // s trở thành "fedcba"

8. Ví dụ thực hành tổng hợp

Ví dụ 1: Chuẩn hóa tên (Viết hoa chữ cái đầu, xóa khoảng trắng thừa)

Bài toán kinh điển trong xử lý xâu: Chuyển chuỗi " nGuYễN vĂn a " thành "Nguyễn Văn A". (Để đơn giản, ví dụ dưới đây minh họa với ký tự không dấu).

C++
#include <iostream>
#include <string>
#include <sstream>
#include <cctype>

using namespace std;

int main() {
    string input = "   lLê    hOàNg    vŨ  ";
    stringstream ss(input); // Tách xâu dựa vào khoảng trắng
    string word;
    string result = "";

    while (ss >> word) {
        // Chuyển chữ cái đầu thành hoa, các chữ sau thành thường
        word[0] = toupper(word[0]);
        for (int i = 1; i < word.length(); i++) {
            word[i] = tolower(word[i]);
        }
        result += word + " ";
    }

    // Xóa dấu cách thừa ở cuối cùng
    if (!result.empty()) {
        result.pop_back();
    }

    cout << "Xâu chuẩn hóa: \"" << result << "\"" << endl;
    // Kết quả: "Lê Hoàng Vũ"
    return 0;
}

Ví dụ 2: Kiểm tra xâu đối xứng (Palindrome)

Xâu đối xứng là xâu đọc từ trái qua phải hay từ phải qua trái đều như nhau (Ví dụ: "radar", "abba").

C++
#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

bool isPalindrome(string s) {
    string rev = s;
    reverse(rev.begin(), rev.end());
    return s == rev;
}

int main() {
    string s;
    cout << "Nhập xâu cần kiểm tra: ";
    cin >> s;

    if (isPalindrome(s)) {
        cout << s << " là xâu đối xứng." << endl;
    } else {
        cout << s << " không phải là xâu đối xứng." << endl;
    }
    return 0;
}

9. Câu hỏi củng cố & Bài tập đề xuất

  1. Phân biệt s.length()sizeof(s). Tại sao không dùng sizeof() cho std::string?
  2. Bài tập 1: Viết chương trình đếm số lượng chữ cái, chữ số và ký tự đặc biệt trong một xâu nhập từ bàn phím.
  3. Bài tập 2: Viết hàm tìm kiếm và thay thế tất cả các từ "anh" thành từ "em" trong một đoạn văn bản.

Bình luận

Gần nhất
Tải bình luận...

Không có bình luận nào.