0

Xâu kí tự trong C++

đã đăng vào 31, Tháng 7, 2026, 22:31

XÂU KÝ TỰ TRONG C++

Chào các bạn học sinh và lập trình viên! Trong các bài toán lập trình thi đấu thực tế trên hệ thống Sen Code Online Judge!, bên cạnh việc xử lý các con số toán học, chúng ta thường xuyên gặp các bài toán liên quan đến văn bản, ký tự như kiểm tra tính hợp lệ của mật khẩu, chuẩn hóa họ tên học sinh, hay xử lý dữ liệu lớn.

Để thực hiện việc này, C++ cung cấp một kiểu dữ liệu cực kỳ mạnh mẽ là Xâu ký tự (std::string). Bài viết này sẽ hướng dẫn chi tiết từ khái niệm cơ bản, cách nhập xuất tối ưu cho đến các hàm xử lý xâu nâng cao kèm ví dụ minh họa trực quan.


1. Khái niệm về xâu ký tự (std::string)

Xâu ký tự (chuỗi ký tự) là một dãy gồm các ký tự liên tiếp nhau. Trong ngôn ngữ C++ hiện đại, thay vì sử dụng mảng ký tự kiểu C cổ điển (char[]) rất dễ gây lỗi tràn bộ nhớ, chúng ta sử dụng lớp std::string nằm trong thư viện chuẩn để quản lý xâu ký tự một cách tự động, an toàn và linh hoạt.


2. Khai báo biến xâu ký tự

Cú pháp:
string tên_xâu; // Khai báo xâu rỗng
string tên_xâu = "giá_trị_khởi_tạo"; // Khai báo và gán giá trị ban đầu

Lưu ý: Hằng xâu ký tự trong C++ luôn được đặt trong cặp dấu nháy kép "" (ví dụ: "Sen Code"), khác với ký tự đơn lẻ đặt trong cặp dấu nháy đơn '' (ví dụ: 'A').


3. Nhập xuất xâu ký tự (CỰC KỲ QUAN TRỌNG)

Thao tác nhập xuất xâu ký tự trong C++ có một số bẫy rất dễ gây lỗi trôi lệnh cho học sinh mới học.

a. Nhập xâu không chứa khoảng trắng (Không chứa dấu cách)

Dùng lệnh cin >> thông thường. Hệ thống sẽ đọc dữ liệu từ luồng nhập và dừng lại khi gặp khoảng trắng, dấu tab hoặc dấu xuống dòng.

string s;
cin >> s; // Nhập "Sen Code" thì s chỉ nhận giá trị "Sen"
b. Nhập xâu chứa khoảng trắng (Đọc cả dòng văn bản)

Để đọc toàn bộ một dòng văn bản bao gồm cả dấu cách, ta sử dụng hàm getline:

string s;
getline(cin, s); // Nhập "Sen Code Online Judge!" thì s nhận đầy đủ cả dòng
c. Bẫy trôi lệnh khi dùng getline và cách xử lý
  • Hiện tượng: Nếu trước khi gọi lệnh getline(cin, s) chương trình có sử dụng cin >> để nhập một số nguyên (ví dụ nhập số lượng bộ test N), ký tự xuống dòng \n sau số nguyên đó vẫn nằm trong bộ đệm. Lệnh getline tiếp theo sẽ lập tức đọc ký tự \n này và kết thúc ngay mà không cho phép người dùng nhập dữ liệu thực tế.
  • Cách xử lý: Sử dụng lệnh cin.ignore() ngay sau câu lệnh cin >> để xóa ký tự xuống dòng khỏi bộ đệm trước khi gọi getline.
Ví dụ minh họa:
#include <bits/stdc++.h>

using namespace std;

int main() {
    int N;
    cin >> N;
    cin.ignore(); // Xóa ký tự xuống dòng còn sót lại trong bộ đệm để tránh trôi lệnh

    string s;
    getline(cin, s); // Nhập cả dòng văn bản tiếp theo

    cout << "So N: " << N << "\n";
    cout << "Xau s: " << s << "\n";
    return 0;
}

4. Các phép toán cơ bản trên xâu

C++ hỗ trợ các toán tử trực quan giúp xử lý xâu dễ dàng như các kiểu dữ liệu số:

  • Phép ghép xâu (+ hoặc +=): Nối hai xâu lại với nhau.

    string a = "Sen ";
    string b = "Code";
    string c = a + b; // c nhận giá trị "Sen Code"
    
  • Phép so sánh (==, !=, <, >): So sánh hai xâu ký tự theo thứ tự từ điển (dựa vào bảng mã ASCII của từng ký tự từ trái qua phải).

    if (a == b) { /* hai xâu giống hệt nhau */ }
    
  • Truy cập ký tự: Ta có thể truy cập hoặc sửa đổi từng ký tự của xâu thông qua chỉ số giống như mảng: s[i]. Chỉ số bắt đầu từ 0 đến s.length() - 1.


5. Các hàm và phương thức xử lý xâu quan trọng

a. Lấy độ dài xâu: s.length() hoặc s.size()

Trả về số lượng ký tự hiện tại của xâu s.

string s = "ABC";
cout << s.length(); // In ra 3
b. Kiểm tra xâu rỗng: s.empty()

Trả về true nếu xâu không chứa ký tự nào, ngược lại trả về false.

c. Trích xuất xâu con: s.substr(pos, len)

Cắt ra một xâu con của s bắt đầu từ vị trí chỉ số pos với độ dài là len ký tự.

string s = "Sen Code";
string sub = s.substr(4, 4); // sub nhận giá trị "Code"
d. Tìm kiếm xâu con: s.find(str)

Tìm vị trí xuất hiện đầu tiên của xâu str trong s.

  • Nếu tìm thấy, trả về chỉ số đầu tiên nơi str xuất hiện.
  • Nếu không tìm thấy, trả về giá trị hằng số đặc biệt string::npos.
string s = "Sen Code";
int pos = s.find("Code"); // pos nhận giá trị 4
if (s.find("Java") == string::npos) {
    cout << "Khong tim thay\n";
}
e. Chèn, Xóa xâu:
  • s.insert(pos, str): Chèn xâu str vào vị trí pos của xâu s.
  • s.erase(pos, len): Xóa len ký tự của xâu s bắt đầu từ vị trí pos.
f. Chuyển đổi giữa Xâu và Số:
  • to_string(num): Chuyển đổi một số nguyên/thực sang xâu ký tự.
  • stoi(s): Chuyển đổi xâu s sang số nguyên kiểu int. (Dùng stoll(s) cho số long longstod(s) cho số thực double).
g. Các hàm xử lý ký tự đơn lẻ (Thư viện <cctype>):

Duyệt qua từng ký tự của xâu c = s[i], ta có thể sử dụng các hàm kiểm tra sau:

  • isdigit(c): Trả về true nếu c là chữ số ('0' - '9').
  • isalpha(c): Trả về true nếu c là chữ cái ('a'-'z', 'A'-'Z').
  • islower(c) / isupper(c): Kiểm tra chữ viết thường / viết hoa.
  • tolower(c) / toupper(c): Trả về ký tự c đã được chuyển sang viết thường / viết hoa.

6. ĐỀ XUẤT 10 BÀI TẬP THỰC HÀNH THỰC TẾ (XÂU KÝ TỰ)

Hãy lập trình đọc/ghi dữ liệu bằng tệp thông qua freopen đối với 10 bài tập ứng dụng thực tế đa dạng lĩnh vực sau đây:

Bài 1: Chuẩn hóa họ tên thành viên
  • Ngữ cảnh: Hệ thống đăng ký tài khoản của Sen Code Online Judge! nhận dữ liệu họ tên do người dùng tự nhập. Do người dùng nhập tự do, họ tên thường bị thừa khoảng trắng ở đầu, cuối, ở giữa các từ và viết hoa lộn xộn. Bạn cần chuẩn hóa họ tên về dạng chuẩn.
  • Yêu cầu: Đọc chuỗi tên từ tệp username.inp, loại bỏ các khoảng trắng thừa ở đầu, cuối và giữa các từ; viết hoa chữ cái đầu tiên của mỗi từ và viết thường các chữ cái còn lại. Ghi kết quả ra tệp username.out.
  • Đầu vào (Input - tệp username.inp): Một dòng duy nhất chứa chuỗi họ tên S (độ dài không quá 1000 ký tự).
  • Đầu ra (Output - tệp username.out): Chuỗi họ tên sau khi đã được chuẩn hóa.
  • Ví dụ:
    • Input: nGUYEn vAn A
    • Output: Nguyen Van A
Bài 2: Kiểm tra mật khẩu mạnh bảo mật hệ thống
  • Ngữ cảnh: Để đăng ký tài khoản, hệ thống an ninh mạng yêu cầu người dùng thiết lập mật khẩu mạnh thỏa mãn các điều kiện:
    1. Độ dài tối thiểu của mật khẩu là 8 ký tự.
    2. Phải chứa ít nhất 1 chữ cái viết hoa (A-Z).
    3. Phải chứa ít nhất 1 chữ cái viết thường (a-z).
    4. Phải chứa ít nhất 1 chữ số (0-9).
  • Yêu cầu: Đọc mật khẩu từ tệp password.inp và ghi kết quả kiểm tra ra tệp password.out.
  • Đầu vào (Input - tệp password.inp): Một chuỗi ký tự S không chứa khoảng trắng (độ dài không quá 100 ký tự).
  • Đầu ra (Output - tệp password.out): In ra STRONG nếu mật khẩu đạt yêu cầu, ngược lại in ra WEAK.
  • Ví dụ:
    • Input: Sencode2026
    • Output: STRONG
Bài 3: Kiểm duyệt từ khóa nhạy cảm
  • Ngữ cảnh: Kiểm duyệt viên cần quét một đoạn văn bản bài viết dài S để tìm kiếm xem có chứa từ khóa nhạy cảm T hay không nhằm ngăn chặn các nội dung không phù hợp.
  • Yêu cầu: Đọc văn bản S và từ khóa T từ tệp censor.inp. Ghi vị trí xuất hiện đầu tiên của từ khóa ra tệp censor.out.
  • Đầu vào (Input - tệp censor.inp):
    • Dòng 1 chứa chuỗi từ khóa nhạy cảm T.
    • Dòng 2 chứa chuỗi văn bản S (độ dài không quá 104 ký tự).
  • Đầu ra (Output - tệp censor.out): Một số nguyên duy nhất là chỉ số (index bắt đầu từ 0) của vị trí xuất hiện đầu tiên của T trong S. Nếu không tìm thấy, in ra -1.
  • Ví dụ:

    • Input:

      cheat
      hoc sinh khong duoc cheat bai thi tren he thong
      
    • Output: 23

Bài 4: Mã hóa tin nhắn quân sự (Mật mã Caesar)
  • Ngữ cảnh: Trong liên lạc quân sự cổ đại, thông tin được mã hóa bằng mật mã Caesar: dịch chuyển mỗi chữ cái trong tin nhắn tiến lên K vị trí trong bảng chữ cái tiếng Anh theo vòng tròn từ A đến Z (và từ a đến z). Các ký tự không phải chữ cái (dấu cách, chữ số, dấu câu) được giữ nguyên.
  • Yêu cầu: Đọc tin nhắn S và bước dịch chuyển K từ tệp caesar.inp, ghi tin nhắn đã mã hóa ra tệp caesar.out.
  • Đầu vào (Input - tệp caesar.inp):
    • Dòng 1 chứa số nguyên dương K (1 <= K <= 25).
    • Dòng 2 chứa chuỗi tin nhắn S cần mã hóa (chứa khoảng trắng, độ dài không quá 1000 ký tự).
  • Đầu ra (Output - tệp caesar.out): Chuỗi tin nhắn sau khi mã hóa.
  • Ví dụ:

    • Input:

      3
      Hello World!
      
    • Output: Khoor Zruog!

Bài 5: Xác thực mã số thẻ định danh
  • Ngữ cảnh: Một chuỗi số đại diện cho mã số định danh của sản phẩm. Mã số được coi là hợp lệ nếu tổng giá trị số học của tất cả các chữ số xuất hiện trong chuỗi là một số chia hết cho 10. Các ký tự không phải số trong chuỗi sẽ bị bỏ qua.
  • Yêu cầu: Đọc chuỗi mã số từ tệp card.inp và ghi kết quả xác thực ra tệp card.out.
  • Đầu vào (Input - tệp card.inp): Một chuỗi ký tự S có thể chứa cả chữ cái và chữ số (độ dài không quá 1000 ký tự).
  • Đầu ra (Output - tệp card.out): In ra VALID nếu hợp lệ, ngược lại in ra INVALID.
  • Ví dụ:
    • Input: ID-123400LL-901
    • Output: VALID (Các chữ số là 1, 2, 3, 4, 0, 0, 9, 0, 1 -> Tổng = 20 chia hết cho 10)
Bài 6: Tìm từ đối xứng (Palindrome)
  • Ngữ cảnh: Một nhà ngôn ngữ học nghiên cứu cấu trúc của các từ đối xứng (Palindrome) - từ đọc xuôi hay ngược đều giống nhau.
  • Yêu cầu: Đọc một từ từ tệp palindrome.inp và ghi kết quả kiểm tra ra tệp palindrome.out.
  • Đầu vào (Input - tệp palindrome.inp): Một chuỗi S viết thường không chứa khoảng trắng (độ dài không quá 1000 ký tự).
  • Đầu ra (Output - tệp palindrome.out): In ra YES nếu S là xâu đối xứng, ngược lại in ra NO.
  • Ví dụ:
    • Input: racecar
    • Output: YES
Bài 7: Nén dữ liệu truyền tin (Run-Length Encoding)
  • Ngữ cảnh: Để tiết kiệm băng thông truyền tin qua vệ tinh, người ta nén chuỗi ký tự bằng thuật toán RLE: đếm số lần lặp liên tiếp của từng ký tự và viết dưới dạng ký tự kèm số lần lặp.
  • Yêu cầu: Nén chuỗi ký tự từ tệp compress.inp ghi ra tệp compress.out.
  • Đầu vào (Input - tệp compress.inp): Một chuỗi S chỉ gồm các chữ cái viết hoa viết liền nhau (độ dài không quá 1000 ký tự).
  • Đầu ra (Output - tệp compress.out): Chuỗi sau khi được nén.
  • Ví dụ:
    • Input: AAABBCDDDD
    • Output: A3B2C1D4
Bài 8: Đếm số lượng từ trong bài viết
  • Ngữ cảnh: Phần mềm soạn thảo văn bản cần thống kê số lượng từ của bài viết để tính nhuận bút cho biên tập viên. Các từ trong văn bản được ngăn cách bởi một hoặc nhiều khoảng trắng (dấu cách).
  • Yêu cầu: Đọc văn bản S từ tệp wordcount.inp và ghi số lượng từ ra tệp wordcount.out.
  • Đầu vào (Input - tệp wordcount.inp): Một dòng văn bản S (độ dài không quá 104 ký tự).
  • Đầu ra (Output - tệp wordcount.out): Một số nguyên duy nhất là số lượng từ của văn bản.
  • Ví dụ:
    • Input: Hoc lap trinh C++ cung Sen Code
    • Output: 7
Bài 9: Trích xuất tên miền Email
  • Ngữ cảnh: Hệ thống phân tích khách hàng cần lọc ra nhà cung cấp dịch vụ email (tên miền đứng sau ký tự @) từ địa chỉ email đầy đủ của người đăng ký.
  • Yêu cầu: Đọc địa chỉ email từ tệp email.inp và ghi tên miền trích xuất được ra tệp email.out.
  • Đầu vào (Input - tệp email.inp): Chuỗi email định dạng username@domain (độ dài không quá 200 ký tự).
  • Đầu ra (Output - tệp email.out): Chuỗi tên miền thu được.
  • Ví dụ:
    • Input: sencode.io.vn@gmail.com
    • Output: gmail.com
Bài 10: Cộng hai số nguyên siêu lớn (Big Integer)
  • Ngữ cảnh: Trong hệ thống bảo mật RSA, ta cần cộng hai số nguyên siêu lớn có hàng ngàn chữ số. Các số này vượt quá giới hạn lưu trữ của kiểu dữ liệu lớn nhất trong C++ (long long). Hãy mô phỏng lại phép toán cộng viết tay đặt tính rồi tính bằng cách xử lý xâu ký tự.
  • Yêu cầu: Đọc hai số lớn A và B từ tệp bigadd.inp và ghi tổng của chúng ra tệp bigadd.out.
  • Đầu vào (Input - tệp bigadd.inp):
    • Dòng 1 chứa số nguyên lớn A.
    • Dòng 2 chứa số nguyên lớn B.
    • (Độ dài số A và B không quá 1000 chữ số, chỉ chứa các chữ số từ 0 đến 9).
  • Đầu ra (Output - tệp bigadd.out): Chuỗi chữ số biểu thị tổng của hai số.
  • Ví dụ:

    • Input:

      99999999999999999999
      1
      
    • Output: 100000000000000000000


Lời kết

Xâu ký tự std::string là một công cụ cực kỳ linh hoạt và xuất hiện liên tục trong lập trình thi đấu. Hãy ghi nhớ bẫy trôi lệnh khi sử dụng getline kết hợp cin >> và luyện tập thuần thục các hàm cắt ghép xâu nhé!

Chúc các bạn rèn luyện thật tốt và đạt thật nhiều điểm xanh lá trên Sen Code Online Judge!! Chúc các bạn thành công!


Bình luận

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.