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> // 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ặ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 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 []
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 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ủ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 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()
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 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()
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 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è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 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()
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> // 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á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> // 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