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

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ọng và thự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ọn và dễ 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 cin
và cout
. 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>
int main() {
string tu, dong;
cout << "Nhap mot tu (vd: XinChao): ";
cin >> tu;
cout << "Ban vua nhap tu: " << tu << '\n';
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Nhap mot dong (vd: Xin chao the gioi): ";
getline(cin, dong);
cout << "Ban vua nhap dong: " << dong << '\n';
return 0;
}
Output:
Nhap mot tu (vd: XinChao): Hello World
Ban vua nhap tu: Hello
Nhap mot dong (vd: Xin chao the gioi): C++ is fun!
Ban vua nhap dong: C++ is fun!
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ặcmyString.size()
: Trả về số lượng ký tự trong chuỗi.myString[index]
hoặcmyString.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 s = "Lap trinh C++";
cout << "Chuoi: '" << s << "'\n";
cout << "Do dai chuoi: " << s.length() << '\n';
cout << "Ky tu dau tien: " << s[0] << '\n';
cout << "Ky tu tai vi tri 4: " << s.at(4) << '\n';
cout << "Cac ky tu: ";
for (int i = 0; i < s.length(); ++i) {
cout << s[i] << " ";
}
cout << '\n';
return 0;
}
Output:
Chuoi: 'Lap trinh C++'
Do dai chuoi: 14
Ky tu dau tien: L
Ky tu tai vi tri 4: t
Cac ky tu: L a p t r i n h C + +
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 []
và .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 p1 = "Xin chao";
string p2 = " the gioi!";
string s = p1 + p2;
cout << "Noi bang +: " << s << '\n';
string m = "Hom nay la ";
m.append("thu Hai.");
cout << "Noi bang append(): " << m << '\n';
m += '!';
cout << "Noi ky tu: " << m << '\n';
return 0;
}
Output:
Noi bang +: Xin chao the gioi!
Noi bang append(): Hom nay la thu Hai.
Noi ky tu: Hom nay la thu Hai.!
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ủasubstring
trongmyString
. Trả về vị trí tìm thấy (kiểusize_t
). Nếu không tìm thấy, trả vềstring::npos
.
#include <iostream>
#include <string>
int main() {
string s = "Hoc lap trinh C++ that la thu vi.";
string tim1 = "C++";
string tim2 = "Java";
size_t viTri = s.find(tim1);
if (viTri != string::npos) {
cout << "Tim thay '" << tim1 << "' tai vi tri: " << viTri << '\n';
} else {
cout << "Khong tim thay '" << tim1 << "' trong chuoi.\n";
}
viTri = s.find(tim2);
if (viTri != string::npos) {
cout << "Tim thay '" << tim2 << "' tai vi tri: " << viTri << '\n';
} else {
cout << "Khong tim thay '" << tim2 << "' trong chuoi.\n";
}
size_t vtKyTu = s.find('t');
if (vtKyTu != string::npos) {
cout << "Ky tu 't' dau tien tai vi tri: " << vtKyTu << '\n';
}
return 0;
}
Output:
Tim thay 'C++' tai vi tri: 14
Khong tim thay 'Java' trong chuoi.
Ky tu 't' dau tien tai vi tri: 4
Giải thích:
Phương thức find()
là 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àilen
.
#include <iostream>
#include <string>
int main() {
string duLieu = "ID:12345-STATUS:Active-USER:Admin";
string id = duLieu.substr(3, 5);
cout << "ID: " << id << '\n';
size_t vtTT = duLieu.find("STATUS:");
if (vtTT != string::npos) {
string tt = duLieu.substr(vtTT + 7, 6);
cout << "STATUS: " << tt << '\n';
}
size_t vtND = duLieu.find("USER:");
if (vtND != string::npos) {
string nd = duLieu.substr(vtND + 5);
cout << "USER: " << nd << '\n';
}
return 0;
}
Output:
ID: 12345
STATUS: Active
USER: Admin
Giải thích:
substr()
là 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()
và 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óalen
ký tự từ vị trípos
và chènreplacementString
vào vị trí đó.
#include <iostream>
#include <string>
int main() {
string s = "Ban nen hoc Java de co viec lam.";
size_t vt = s.find("Java");
if (vt != string::npos) {
s.replace(vt, 4, "C++");
cout << "Sau khi thay the: " << s << '\n';
} else {
cout << "Khong tim thay tu can thay the.\n";
}
return 0;
}
Output:
Sau khi thay the: Ban nen hoc C++ de co viec lam.
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ènstringToInsert
vào vị trípos
.myString.erase(pos, len)
: Xóalen
ký tự bắt đầu từ vị trípos
. Nếu bỏ qualen
, sẽ xóa đến hết chuỗi.
#include <iostream>
#include <string>
int main() {
string s = "Day la chuoi goc.";
s.insert(7, "mot ");
cout << "Sau khi chen: " << s << '\n';
s.erase(4, 3);
cout << "Sau khi xoa: " << s << '\n';
s.erase(4);
cout << "Sau khi xoa tu vi tri 4 den het: " << s << '\n';
return 0;
}
Output:
Sau khi chen: Day la mot chuoi goc.
Sau khi xoa: Day mot chuoi goc.
Sau khi xoa tu vi tri 4 den het: Day
Giải thích:
insert()
và 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>
int main() {
string s1 = "";
string s2 = "Hello";
string nhap;
if (s1.empty()) {
cout << "s1 la chuoi rong.\n";
} else {
cout << "s1 khong rong.\n";
}
if (s2.empty()) {
cout << "s2 la chuoi rong.\n";
} else {
cout << "s2 khong rong.\n";
}
cout << "Nhap gi do (hoac enter ngay de bo trong): ";
getline(cin, nhap);
if (nhap.empty()) {
cout << "Ban da nhap mot dong rong.\n";
} else {
cout << "Ban da nhap dong: '" << nhap << "'\n";
}
return 0;
}
Output:
s1 la chuoi rong.
s2 khong rong.
Nhap gi do (hoac enter ngay de bo trong):
Ban da nhap mot dong rong.
Giải thích:
Phương thức empty()
là cách chính xác và rõ 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>
#include <stdexcept>
int main() {
string sSo = "123";
string sThuc = "3.14159";
string sSai = "abc";
int tuoi = 25;
double gia = 99.99;
try {
int so = stoi(sSo);
double thuc = stod(sThuc);
// int errorSo = stoi(sSai); // Uncomment de xem loi
cout << "Chuoi '" << sSo << "' thanh so nguyen: " << so << '\n';
cout << "Chuoi '" << sThuc << "' thanh so thuc: " << thuc << '\n';
} catch (const invalid_argument& ia) {
cerr << "Loi doi so: " << ia.what() << '\n';
} catch (const out_of_range& oor) {
cerr << "Loi ngoai pham vi: " << oor.what() << '\n';
}
string sTuoi = to_string(tuoi);
string sGia = to_string(gia);
cout << "So nguyen " << tuoi << " thanh chuoi: '" << sTuoi << "'\n";
cout << "So thuc " << gia << " thanh chuoi: '" << sGia << "'\n";
cout << "Toi " + sTuoi + " tuoi.\n";
return 0;
}
Output:
Chuoi '123' thanh so nguyen: 123
Chuoi '3.14159' thanh so thuc: 3.14159
So nguyen 25 thanh chuoi: '25'
So thuc 99.99 thanh chuoi: '99.990000'
Toi 25 tuoi.
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