Bài 28.2: Các phương thức xử lý chuỗi ký tự trong C++

Bài 28.2: Các phương thức xử lý chuỗi ký tự trong C++
Trong lập trình C++, việc làm việc với văn bản, hay còn gọi là chuỗi ký tự, là một kỹ năng cốt lõi. Lớp string
trong Thư viện Chuẩn C++ (STL) cung cấp một bộ công cụ mạnh mẽ để tạo, chỉnh sửa, tìm kiếm và thao tác với chuỗi một cách hiệu quả và an toàn hơn nhiều so với mảng ký tự kiểu C truyền thống.
Bài viết này sẽ giới thiệu và đi sâu vào các phương thức (methods) chính mà string
mang lại, giúp bạn khai thác tối đa khả năng xử lý chuỗi trong các dự án C++ của mình. Hãy cùng khám phá sức mạnh thực sự của string
!
Để sử dụng string
, bạn cần include header <string>
.
#include <iostream>
#include <string>
int main() {
string s = "Chào mừng đến với thế giới chuỗi trong C++!";
cout << s << endl;
return 0;
}
Output:
Chào mừng đến với thế giới chuỗi trong C++!
Giờ thì, hãy đi vào chi tiết các phương thức hữu ích nhé!
1. Truy cập và Lấy Thông tin Cơ bản về Chuỗi
Chúng ta thường cần biết chuỗi dài bao nhiêu hoặc truy cập vào một ký tự cụ thể.
Phương thức length()
và size()
Hai phương thức này đều trả về số lượng ký tự trong chuỗi. Chúng thường có cùng chức năng trong hầu hết các trường hợp sử dụng string
.
#include <iostream>
#include <string>
int main() {
string s = "Xin chào!";
cout << "Chuỗi: " << s << endl;
cout << "Độ dài: " << s.length() << endl;
cout << "Kích thước: " << s.size() << endl;
return 0;
}
Output:
Chuỗi: Xin chào!
Độ dài: 9
Kích thước: 9
Giải thích: Code trên khai báo một chuỗi greeting
và sử dụng length()
và size()
để in ra số ký tự của chuỗi đó. Lưu ý rằng "Xin chào!" có 9 ký tự (bao gồm cả dấu chấm than).
Truy cập ký tự bằng toán tử []
và phương thức at()
Bạn có thể truy cập từng ký tự riêng lẻ trong chuỗi bằng cách sử dụng chỉ số (index). Chỉ số bắt đầu từ 0.
- Toán tử
[]
: Cung cấp cách truy cập nhanh nhưng không kiểm tra biên (out-of-bounds checking). Nếu bạn truy cập vào một chỉ số không hợp lệ, chương trình có thể gặp lỗi hoặc có hành vi không xác định. - Phương thức
at()
: Tương tự như[]
nhưng có kiểm tra biên. Nếu chỉ số không hợp lệ, nó sẽ ném ra một ngoại lệ (out_of_range
), giúp bạn phát hiện lỗi dễ dàng hơn.
#include <iostream>
#include <string>
#include <stdexcept>
int main() {
using namespace std;
string s = "Hello";
cout << "Chuỗi: " << s << endl;
cout << "Ký tự đầu tiên (dùng []): " << s[0] << endl;
cout << "Ký tự thứ ba (dùng at()): " << s.at(2) << endl;
try {
cout << "Thử truy cập index 10 dùng at(): ";
cout << s.at(10) << endl;
} catch (const out_of_range& oor) {
cerr << "Bắt được lỗi: Truy cập ngoài biên: " << oor.what() << endl;
}
return 0;
}
Output:
Chuỗi: Hello
Ký tự đầu tiên (dùng []): H
Ký tự thứ ba (dùng at()): l
Thử truy cập index 10 dùng at(): Bắt được lỗi: Truy cập ngoài biên: basic_string::at: __n (which is 10) >= this->size() (which is 5)
Giải thích: Ví dụ cho thấy cách truy cập ký tự đầu tiên (H
ở index 0) và ký tự thứ ba (l
ở index 2). Phần try-catch
minh họa cách at()
xử lý truy cập index không hợp lệ bằng cách ném ngoại lệ out_of_range
.
Phương thức empty()
Kiểm tra xem chuỗi có rỗng hay không (không chứa ký tự nào). Trả về true
nếu chuỗi rỗng, false
nếu ngược lại.
#include <iostream>
#include <string>
int main() {
string s1 = "";
string s2 = "Không rỗng";
if (s1.empty()) {
cout << "s1 là chuỗi rỗng." << endl;
}
if (!s2.empty()) {
cout << "s2 không phải là chuỗi rỗng." << endl;
}
return 0;
}
Output:
s1 là chuỗi rỗng.
s2 không phải là chuỗi rỗng.
Giải thích: s1
được khởi tạo là chuỗi rỗng, nên s1.empty()
trả về true
. s2
có nội dung, nên s2.empty()
trả về false
.
2. Thay đổi Nội dung Chuỗi
Một trong những sức mạnh của string
là khả năng dễ dàng thay đổi nội dung của nó.
Nối chuỗi (Concatenation)
Có nhiều cách để nối các chuỗi lại với nhau.
- Toán tử
+
và+=
: Cách phổ biến và trực quan nhất. - Phương thức
append()
: Linh hoạt hơn, cho phép nối toàn bộ hoặc một phần của chuỗi khác, hoặc lặp lại một ký tự nhiều lần.
#include <iostream>
#include <string>
int main() {
using namespace std;
string s1 = "Xin ";
string s2 = "chào";
string s3 = " các bạn!";
string kq1 = s1 + s2 + s3;
cout << "Kết hợp dùng +: " << kq1 << endl;
string kq2 = s1;
kq2 += s2;
kq2 += s3;
cout << "Kết hợp dùng +=: " << kq2 << endl;
string kq3 = "Hello";
kq3.append(" World");
kq3.append("!");
cout << "Kết hợp dùng append(): " << kq3 << endl;
return 0;
}
Output:
Kết hợp dùng +: Xin chào các bạn!
Kết hợp dùng +=: Xin chào các bạn!
Kết hợp dùng append(): Hello World!
Giải thích: Ví dụ minh họa ba cách thông dụng để nối các chuỗi. Toán tử +
tạo ra chuỗi mới, +=
và append()
thay đổi trực tiếp chuỗi gốc (hoặc chuỗi được gán ban đầu).
Chèn chuỗi (Insertion)
Phương thức insert()
cho phép bạn chèn một chuỗi khác hoặc một ký tự vào một vị trí cụ thể trong chuỗi hiện tại.
#include <iostream>
#include <string>
int main() {
string s = "Học C++ rất.";
string chen = " hay và thú vị";
s.insert(s.find("rất") + 3, chen);
cout << "Sau khi chèn: " << s << endl;
return 0;
}
Output:
Sau khi chèn: Học C++ rất hay và thú vị.
Giải thích: Code tìm vị trí của từ "rất" trong chuỗi, sau đó dùng insert()
để chèn chuỗi to_insert
vào ngay sau từ "rất" (cộng thêm 3 ký tự để vượt qua "rất.").
Xóa ký tự/chuỗi con (Erasing)
Phương thức erase()
cho phép xóa một hoặc nhiều ký tự khỏi chuỗi, bắt đầu từ một vị trí cụ thể và với số lượng ký tự nhất định.
#include <iostream>
#include <string>
int main() {
string s = "Đây là một câu thừa từ cần xóa.";
size_t pos = s.find(" thừa");
if (pos != string::npos) {
s.erase(pos, 5);
}
cout << "Sau khi xóa: " << s << endl;
return 0;
}
Output:
Sau khi xóa: Đây là một câu từ cần xóa.
Giải thích: Chúng ta tìm vị trí của chuỗi con " thừa". Nếu tìm thấy (kiểm tra != string::npos
), ta dùng erase()
để xóa 5 ký tự bắt đầu từ vị trí đó, loại bỏ " thừa". string::npos
là một giá trị đặc biệt biểu thị "không tìm thấy".
Thay thế chuỗi con (Replacing)
Phương thức replace()
cho phép thay thế một phần của chuỗi bằng một chuỗi khác.
#include <iostream>
#include <string>
int main() {
string s = "Tôi thích táo.";
string cu = "táo";
string moi = "cam";
size_t pos = s.find(cu);
if (pos != string::npos) {
s.replace(pos, cu.length(), moi);
}
cout << "Sau khi thay thế: " << s << endl;
return 0;
}
Output:
Sau khi thay thế: Tôi thích cam.
Giải thích: Code tìm vị trí của "táo". Nếu tìm thấy, nó dùng replace()
để thay thế một đoạn chuỗi bắt đầu từ vị trí đó, có độ dài bằng độ dài của "táo" (old_word.length()
), bằng chuỗi new_word
("cam").
Xóa toàn bộ nội dung (Clearing)
Phương thức clear()
sẽ xóa tất cả các ký tự trong chuỗi, làm cho chuỗi trở nên rỗng.
#include <iostream>
#include <string>
int main() {
string s = "Chuỗi này sẽ bị xóa.";
cout << "Chuỗi ban đầu: '" << s << "'" << endl;
s.clear();
cout << "Sau khi clear: '" << s << "'" << endl;
if (s.empty()) {
cout << "Chuỗi đã rỗng." << endl;
}
return 0;
}
Output:
Chuỗi ban đầu: 'Chuỗi này sẽ bị xóa.'
Sau khi clear: ''
Chuỗi đã rỗng.
Giải thích: clear()
làm cho chuỗi my_string
không còn ký tự nào. Sau khi gọi clear()
, my_string.empty()
sẽ trả về true
.
3. Tìm kiếm trong Chuỗi
string
cung cấp các phương thức mạnh mẽ để tìm kiếm sự xuất hiện của các chuỗi con.
Phương thức find()
và rfind()
find()
: Tìm vị trí xuất hiện đầu tiên của một chuỗi con hoặc ký tự, bắt đầu từ đầu chuỗi (hoặc từ một vị trí cụ thể nếu được chỉ định).rfind()
: Tìm vị trí xuất hiện cuối cùng của một chuỗi con hoặc ký tự, bắt đầu từ cuối chuỗi (hoặc từ một vị trí cụ thể nếu được chỉ định).
Cả hai phương thức trả về chỉ số (index) của lần xuất hiện đầu tiên/cuối cùng. Nếu không tìm thấy, chúng trả về string::npos
.
#include <iostream>
#include <string>
int main() {
using namespace std;
string s = "Tìm kiếm từ trong chuỗi này, tìm từ khóa.";
string tu_khoa = "tìm";
string tu_khong_co = "Python";
size_t pos1 = s.find(tu_khoa);
if (pos1 != string::npos) {
cout << "Tìm thấy '" << tu_khoa << "' lần đầu ở vị trí: " << pos1 << endl;
} else {
cout << "Không tìm thấy '" << tu_khoa << "' lần đầu." << endl;
}
size_t pos2 = s.rfind(tu_khoa);
if (pos2 != string::npos) {
cout << "Tìm thấy '" << tu_khoa << "' lần cuối ở vị trí: " << pos2 << endl;
} else {
cout << "Không tìm thấy '" << tu_khoa << "' lần cuối." << endl;
}
size_t pos3 = s.find(tu_khong_co);
if (pos3 != string::npos) {
cout << "Tìm thấy '" << tu_khong_co << "' ở vị trí: " << pos3 << endl;
} else {
cout << "Không tìm thấy '" << tu_khong_co << "'." << endl;
}
return 0;
}
Output:
Tìm thấy 'tìm' lần đầu ở vị trí: 0
Tìm thấy 'tìm' lần cuối ở vị trí: 28
Không tìm thấy 'Python'.
Giải thích: find("tìm")
trả về 0 vì "Tìm" (không phân biệt hoa thường) là từ đầu tiên. rfind("tìm")
trả về 28, vị trí của "tìm" trong "tìm từ khóa". Khi tìm "Python", find()
trả về string::npos
, dẫn đến thông báo không tìm thấy.
4. Trích xuất Chuỗi con (Substring)
Phương thức substr()
cho phép bạn tạo một chuỗi mới chứa một phần của chuỗi hiện tại.
#include <iostream>
#include <string>
int main() {
string u = "https://fullhousetesting.web.app/bai-viet/cpp-strings";
string mien = u.substr(8, 17);
cout << "Domain: " << mien << endl;
string duong_dan = u.substr(26);
cout << "Path: " << duong_dan << endl;
return 0;
}
Output:
Domain: fullhousetesting.web.app
Path: /bai-viet/cpp-strings
Giải thích: substr()
có hai tham số: vị trí bắt đầu và độ dài (tùy chọn). Nếu không truyền độ dài, nó sẽ lấy từ vị trí bắt đầu đến hết chuỗi.
5. So sánh Chuỗi (Comparison)
Bạn có thể so sánh các chuỗi bằng các toán tử so sánh thông thường (==
, !=
, <
, >
, <=
, >=
) hoặc bằng phương thức compare()
. So sánh dựa trên thứ tự từ điển (lexicographical order).
- Toán tử so sánh: Trả về
true
hoặcfalse
. Dễ đọc và sử dụng cho các phép so sánh bằng/khác và so sánh thứ tự đơn giản. - Phương thức
compare()
: Trả về một số nguyên:0
nếu hai chuỗi bằng nhau.- Một số âm nếu chuỗi gọi phương thức "nhỏ hơn" chuỗi đối số.
- Một số dương nếu chuỗi gọi phương thức "lớn hơn" chuỗi đối số.
compare()
hữu ích khi bạn cần biết mối quan hệ thứ tự cụ thể giữa hai chuỗi.
#include <iostream>
#include <string>
int main() {
using namespace std;
string s1 = "apple";
string s2 = "banana";
string s3 = "apple";
if (s1 == s3) {
cout << s1 << " bằng " << s3 << endl;
}
if (s1 != s2) {
cout << s1 << " khác " << s2 << endl;
}
if (s1 < s2) {
cout << s1 << " nhỏ hơn " << s2 << " (theo thứ tự từ điển)" << endl;
}
cout << s1 << ".compare(" << s3 << ") = " << s1.compare(s3) << endl;
cout << s1 << ".compare(" << s2 << ") = " << s1.compare(s2) << endl;
cout << s2 << ".compare(" << s1 << ") = " << s2.compare(s1) << endl;
return 0;
}
Output:
apple bằng apple
apple khác banana
apple nhỏ hơn banana (theo thứ tự từ điển)
apple.compare(apple) = 0
apple.compare(banana) = -1
banana.compare(apple) = 1
Giải thích: Ví dụ cho thấy cả hai cách so sánh. Toán tử dễ dùng cho ==
, !=
, <
... compare()
cung cấp thông tin chi tiết hơn về mối quan hệ thứ tự (bằng, nhỏ hơn, lớn hơn).
Bài tập ví dụ: C++ Bài 16.A2: Trò chơi khối
Trò chơi khối
FullHouse Dev đang chơi một trò chơi phổ biến ở Byteland. Họ có các khối, mỗi khối biểu thị một số nguyên từ 0 đến 9. Các khối này được sắp xếp ngẫu nhiên để tạo thành các số khác nhau, với điều kiện khối đầu tiên không bao giờ là 0. Sau khi tạo thành một số, họ đọc theo thứ tự ngược lại để kiểm tra xem số đó và số đảo ngược của nó có giống nhau không. Nếu giống nhau, người chơi thắng. Những số như vậy được gọi là số đối xứng.
Yêu cầu
Viết một chương trình để:
- Nhận đầu vào từ người dùng.
- Kiểm tra xem số đó có phải là số đối xứng hay không.
- Thông báo kết quả (thắng hoặc thua).
Input
- Dòng đầu tiên chứa T, số lượng test case.
- T dòng tiếp theo, mỗi dòng chứa một số nguyên N.
Output
- Với mỗi test case, in ra "wins" nếu số đó là số đối xứng, ngược lại in ra "loses" trên một dòng mới.
Ràng buộc
- 1 ≤ T ≤ 20
- 1 ≤ N ≤ 20000
Ví dụ
Input:
3
331
666
343
Output:
loses
wins
wins
Giải thích
- 331 không phải số đối xứng (331 ≠ 133), nên kết quả là "loses".
- 666 là số đối xứng, nên kết quả là "wins".
- 343 là số đối xứng, nên kết quả là "wins". Chào bạn,
Đây là hướng dẫn giải bài "Trò chơi khối" bằng C++ sử dụng các thư viện chuẩn (std) để kiểm tra xem một số nguyên dương có phải là số đối xứng hay không.
Ý tưởng chính:
Một số nguyên là số đối xứng nếu nó đọc giống nhau khi đọc từ trái sang phải và từ phải sang trái. Cách đơn giản nhất để kiểm tra điều này là so sánh số ban đầu với số đảo ngược của nó.
Đối với số nguyên, việc đảo ngược trực tiếp có thể hơi phức tạp (phải bóc tách từng chữ số). Một phương pháp phổ biến và khá tiện lợi khi làm việc với các chữ số của một số là chuyển số đó thành dạng chuỗi ký tự. Sau đó, đảo ngược chuỗi ký tự đó và so sánh với chuỗi ban đầu.
Các bước thực hiện:
- Đọc số lượng test case: Đầu tiên, chương trình cần đọc số lượng test case
T
từ đầu vào. - Vòng lặp cho từng test case: Sử dụng một vòng lặp (ví dụ:
while
hoặcfor
) để xử lýT
lần. - Đọc số N: Bên trong vòng lặp, đọc số nguyên
N
cho mỗi test case. - Chuyển số N thành chuỗi: Sử dụng hàm có sẵn trong thư viện chuẩn C++ để chuyển đổi số nguyên
N
thành một đối tượng chuỗi (ví dụ:string
). Đây là bước quan trọng để có thể dễ dàng thao tác với các chữ số dưới dạng ký tự. - Tạo một chuỗi đảo ngược: Tạo một bản sao của chuỗi vừa thu được. Sử dụng một hàm hoặc thuật toán có sẵn trong thư viện chuẩn C++ để đảo ngược chuỗi bản sao này.
- So sánh chuỗi ban đầu và chuỗi đảo ngược: So sánh chuỗi ban đầu (biểu diễn số N) với chuỗi đã bị đảo ngược.
- In kết quả:
- Nếu hai chuỗi giống hệt nhau, tức là số
N
là số đối xứng, in ra "wins". - Ngược lại, nếu hai chuỗi khác nhau, in ra "loses".
- Nếu hai chuỗi giống hệt nhau, tức là số
- Kết thúc vòng lặp: Lặp lại các bước 3-7 cho đến khi xử lý hết
T
test case.
Gợi ý sử dụng thư viện std
:
- Để đọc và ghi dữ liệu: Sử dụng
cin
vàcout
từ header<iostream>
. - Để làm việc với chuỗi: Sử dụng
string
từ header<string>
. - Để chuyển số thành chuỗi: Sử dụng
to_string()
từ header<string>
. - Để đảo ngược chuỗi: Sử dụng hàm
reverse()
từ header<algorithm>
. Hàm này hoạt động trên các bộ lặp (iterator), bạn có thể dùngchuoi.begin()
vàchuoi.end()
để đảo ngược toàn bộ chuỗi.
#include <iostream>
#include <string>
#include <algorithm> // For reverse
int main() {
ios_base::sync_with_stdio(false); // Tối ưu I/O
cin.tie(NULL); // Bỏ ràng buộc cin với cout
int t;
cin >> t; // Đọc số lượng test case
while (t--) { // Lặp t lần
int n;
cin >> n; // Đọc số nguyên N
string s = to_string(n); // Chuyển N thành chuỗi
string rs = s; // Tạo bản sao để đảo ngược
reverse(rs.begin(), rs.end()); // Đảo ngược chuỗi
if (s == rs) { // So sánh chuỗi gốc và chuỗi đảo ngược
cout << "wins\n";
} else {
cout << "loses\n";
}
}
return 0;
}
Output cho input ví dụ:
loses
wins
wins
Comments