Bài 29.3: Tạo email từ chuỗi trong C++

Bài 29.3: Tạo email từ chuỗi trong C++
Chào mừng các bạn quay trở lại với chuỗi bài blog về C++!
Trong thế giới lập trình, việc làm việc với dữ liệu dưới dạng văn bản, đặc biệt là các chuỗi ký tự, là vô cùng phổ biến. Một trong những loại dữ liệu có cấu trúc thường gặp nhất được biểu diễn dưới dạng chuỗi chính là địa chỉ email. Từ việc thu thập thông tin người dùng đến xử lý dữ liệu danh bạ, chúng ta đều cần biết cách tạo, kiểm tra và trích xuất thông tin từ các chuỗi email.
Bài viết hôm nay, Bài 29.3, sẽ đưa chúng ta đi sâu vào cách thực hiện những thao tác này với chuỗi trong C++. Chúng ta sẽ không chỉ học cách kết hợp các phần lại để tạo ra một địa chỉ email hoàn chỉnh, mà còn tìm hiểu cách kiểm tra xem một chuỗi có trông giống định dạng email hay không, và làm sao để tách username và domain ra khỏi chuỗi đó.
Hãy cùng bắt đầu nhé!
Địa chỉ Email - Cấu trúc cơ bản dưới dạng chuỗi
Một địa chỉ email thông thường có cấu trúc username@domain.tld
. Ví dụ: tennguoidung@tencongty.com
.
Trong C++, chúng ta biểu diễn địa chỉ email này bằng một đối tượng **string**
.
#include <iostream>
#include <string>
int main() {
string em = "example.user@mail.com";
cout << "Địa chỉ email: " << em << endl;
return 0;
}
Địa chỉ email: example.user@mail.com
Đoạn code đơn giản trên chỉ cho thấy cách lưu trữ một chuỗi email. Tuy nhiên, làm thế nào để tạo chuỗi này từ các phần riêng lẻ, hoặc phân tích nó ra? Đó là nội dung chính của bài này.
1. Tạo chuỗi email từ các phần riêng lẻ
Thường thì chúng ta không có sẵn chuỗi email hoàn chỉnh mà cần xây dựng nó từ các thông tin đã có, ví dụ như tên người dùng và tên miền. Trong C++, cách đơn giản nhất để kết hợp các chuỗi lại với nhau là sử dụng toán tử cộng (+
).
Giả sử bạn có tên người dùng là "nguyenvana" và tên miền là "gmail.com". Bạn muốn tạo địa chỉ email nguyenvana@gmail.com
.
#include <iostream>
#include <string>
int main() {
string ten = "nguyenvana";
string ten_mien = "gmail.com";
string em = ten + "@" + ten_mien;
cout << "Username: " << ten << endl;
cout << "Domain: " << ten_mien << endl;
cout << "Địa chỉ email tạo ra: " << em << endl;
return 0;
}
Username: nguyenvana
Domain: gmail.com
Địa chỉ email tạo ra: nguyenvana@gmail.com
Giải thích:
- Chúng ta khai báo ba chuỗi riêng biệt:
username
,domain
, và ký tự@
. - Sử dụng toán tử
+
, chúng ta nốiusername
,at_symbol
, vàdomain
theo đúng thứ tự để tạo thành chuỗiemail
hoàn chỉnh. string
trong C++ hỗ trợ quá tải (overload) toán tử+
để thực hiện phép nối chuỗi một cách trực quan.
Bạn cũng có thể kết hợp nhiều phần hơn. Ví dụ, tạo email từ họ, tên, và tên miền:
#include <iostream>
#include <string>
int main() {
string ho = "le";
string ten = "thi";
string ten_dem = "b";
string mien = "example.net";
string ten_dn = ho + ten_dem + "." + ten;
string em = ten_dn + "@" + mien;
cout << "First Name: " << ho << endl;
cout << "Last Name: " << ten << endl;
cout << "Middle Name: " << ten_dem << endl;
cout << "Domain: " << mien << endl;
cout << "Username tạo ra: " << ten_dn << endl;
cout << "Địa chỉ email tạo ra: " << em << endl;
return 0;
}
First Name: le
Last Name: thi
Middle Name: b
Domain: example.net
Username tạo ra: letb.thi
Địa chỉ email tạo ra: letb.thi@example.net
Trong ví dụ này, chúng ta tạo username
bằng cách nối tên và thêm dấu chấm, sau đó mới nối username
với @
và domain
để có email cuối cùng. Toán tử +
cho phép bạn nối nhiều chuỗi hoặc ký tự (char
) lại với nhau trong một biểu thức duy nhất.
2. Kiểm tra định dạng cơ bản của chuỗi email
Sau khi tạo (hoặc nhận được) một chuỗi email, chúng ta thường cần kiểm tra xem nó có tuân thủ định dạng cơ bản của một địa chỉ email hay không. Điều này giúp tránh xử lý các dữ liệu rác hoặc sai cấu trúc.
Quan trọng: Kiểm tra định dạng email đầy đủ và chính xác theo tiêu chuẩn RFC là một nhiệm vụ phức tạp, thường yêu cầu sử dụng Biểu thức chính quy (Regular Expressions). Trong bài này, chúng ta sẽ chỉ thực hiện một số kiểm tra cơ bản nhất bằng các hàm xử lý chuỗi của string
.
Các kiểm tra cơ bản có thể bao gồm:
- Chuỗi có chứa ký tự
@
không? - Chuỗi có chứa ký tự
.
không? - Ký tự
@
có xuất hiện trước ký tự.
đầu tiên không? - Ký tự
@
có phải là ký tự đầu tiên hoặc cuối cùng không? (Tương tự với.
)
Chúng ta có thể sử dụng hàm **string::find**
để tìm vị trí của một ký tự hoặc một chuỗi con trong chuỗi. Hàm này trả về chỉ số (index) của lần xuất hiện đầu tiên, hoặc **string::npos**
nếu không tìm thấy.
Hãy xây dựng một hàm kiểm tra cơ bản:
#include <iostream>
#include <string>
bool kiem_tra_email_co_ban(const string& s) {
size_t pos_at = s.find('@');
if (pos_at == string::npos || pos_at == 0 || pos_at == s.length() - 1) {
return false;
}
size_t pos_cham = s.find('.', pos_at + 1);
if (pos_cham == string::npos || pos_cham == s.length() - 1) {
return false;
}
if (pos_cham <= pos_at + 1) {
return false;
}
return true;
}
int main() {
string email1 = "test@example.com";
string email2 = "invalid-email";
string email3 = "user@domain";
string email4 = "@domain.com";
string email5 = "user@domain.";
string email6 = "user@.com";
cout << email1 << " is valid basic? " << (kiem_tra_email_co_ban(email1) ? "Yes" : "No") << endl;
cout << email2 << " is valid basic? " << (kiem_tra_email_co_ban(email2) ? "Yes" : "No") << endl;
cout << email3 << " is valid basic? " << (kiem_tra_email_co_ban(email3) ? "Yes" : "No") << endl;
cout << email4 << " is valid basic? " << (kiem_tra_email_co_ban(email4) ? "Yes" : "No") << endl;
cout << email5 << " is valid basic? " << (kiem_tra_email_co_ban(email5) ? "Yes" : "No") << endl;
cout << email6 << " is valid basic? " << (kiem_tra_email_co_ban(email6) ? "Yes" : "No") << endl;
return 0;
}
test@example.com is valid basic? Yes
invalid-email is valid basic? No
user@domain is valid basic? No
@domain.com is valid basic? No
user@domain. is valid basic? No
user@.com is valid basic? No
Giải thích:
- Hàm
isValidEmailBasic
nhận một chuỗi email dưới dạng tham chiếu hằng (const string&
) để tránh sao chép và đảm bảo không thay đổi chuỗi gốc. email.find('@')
: Tìm vị trí đầu tiên của ký tự@
. Nếu không tìm thấy, nó trả vềstring::npos
.- Chúng ta kiểm tra nếu
at_pos
làstring::npos
hoặc@
ở vị trí 0 hoặc@
ở vị trí cuối cùng (email.length() - 1
), thì chuỗi không hợp lệ cơ bản và trả vềfalse
. email.find('.', at_pos + 1)
: Tìm vị trí đầu tiên của ký tự.
bắt đầu tìm từ vị trí ngay sau@
. Điều này đảm bảo dấu chấm chúng ta tìm thấy nằm trong phần domain.- Tương tự, chúng ta kiểm tra
dot_pos
. Nếu không tìm thấy dấu chấm sau@
hoặc dấu chấm đó ở vị trí cuối cùng của chuỗi, trả vềfalse
. - Kiểm tra
dot_pos <= at_pos + 1
đảm bảo rằng phải có ít nhất một ký tự giữa@
và dấu.
đầu tiên trong domain. - Nếu tất cả các kiểm tra này đều vượt qua, chúng ta coi chuỗi đó là có định dạng email cơ bản và trả về
true
.
Hàm này chỉ là bước kiểm tra ban đầu. Một địa chỉ email hợp lệ có thể chứa các ký tự đặc biệt, dấu gạch ngang, số, v.v., theo các quy tắc phức tạp hơn. Đối với các ứng dụng thực tế yêu cầu độ chính xác cao, bạn nên tìm hiểu về Regular Expressions trong C++ (thư viện <regex>
).
3. Trích xuất Username và Domain
Khi bạn có một chuỗi email, bạn có thể muốn tách nó thành hai phần chính: username (phần trước @
) và domain (phần sau @
). Chúng ta có thể làm điều này bằng cách sử dụng hàm **string::substr**
kết hợp với **string::find**
để xác định vị trí của ký tự @
.
Hàm substr(pos, len)
của string
trả về một chuỗi con bắt đầu từ vị trí pos
và có độ dài len
. Nếu bỏ qua len
, nó sẽ trả về chuỗi con từ pos
đến hết chuỗi gốc.
#include <iostream>
#include <string>
int main() {
string em = "user.name123@sub.domain.co.uk";
size_t pos_at = em.find('@');
if (pos_at != string::npos) {
string ten_dn = em.substr(0, pos_at);
string mien = em.substr(pos_at + 1);
cout << "Chuỗi email gốc: " << em << endl;
cout << "Username: " << ten_dn << endl;
cout << "Domain: " << mien << endl;
} else {
cout << "Chuỗi \"" << em << "\" không phải là định dạng email hợp lệ (thiếu '@')." << endl;
}
return 0;
}
Chuỗi email gốc: user.name123@sub.domain.co.uk
Username: user.name123
Domain: sub.domain.co.uk
Giải thích:
- Chúng ta dùng
email.find('@')
để lấy vị trí của ký tự@
. - Nếu
at_pos
không phải làstring::npos
(nghĩa là@
có tồn tại trong chuỗi):email.substr(0, at_pos)
: Tạo chuỗi con bắt đầu từ vị trí 0 (đầu chuỗi) và có độ dài bằngat_pos
. Đây chính là phần username.email.substr(at_pos + 1)
: Tạo chuỗi con bắt đầu từ vị tríat_pos + 1
(vị trí ngay sau@
) và lấy tất cả các ký tự còn lại đến hết chuỗi. Đây chính là phần domain.
- Nếu
@
không được tìm thấy, chúng ta in ra thông báo lỗi.
Ví dụ khác với chuỗi email đơn giản hơn:
#include <iostream>
#include <string>
int main() {
string em = "admin@website.org";
size_t pos_at = em.find('@');
if (pos_at != string::npos) {
string ten_dn = em.substr(0, pos_at);
string mien = em.substr(pos_at + 1);
cout << "Email: " << em << endl;
cout << "Username: " << ten_dn << endl;
cout << "Domain: " << mien << endl;
} else {
cout << "Chuỗi \"" << em << "\" không chứa '@'." << endl;
}
return 0;
}
Email: admin@website.org
Username: admin
Domain: website.org
Kỹ thuật sử dụng find
và substr
không chỉ áp dụng cho email mà còn rất hữu ích để phân tích các chuỗi có định dạng nhất định, sử dụng các ký tự phân cách (delimiter) như dấu phẩy (,), dấu hai chấm (:), dấu gạch ngang (-), v.v.
Bài tập ví dụ: C++ Bài 18.A3: Tránh tiếp xúc
Tránh tiếp xúc
FullHouse Dev đang quản lý một ký túc xá có N phòng xếp thành một hàng thẳng. Họ cần bố trí chỗ ở cho X người, trong đó có Y người bị nhiễm thủy đậu. Để đảm bảo an toàn, họ phải tuân thủ các quy tắc sau:
- Không ai được ở trong phòng kề cạnh phòng của người bị nhiễm thủy đậu.
- Hai người bị nhiễm thủy đậu không thể ở trong hai phòng kề nhau.
Hãy giúp FullHouse Dev tìm ra giá trị N nhỏ nhất để có thể bố trí chỗ ở cho tất cả mọi người theo quy tắc trên.
INPUT FORMAT
- Dòng đầu tiên chứa số nguyên T — số lượng bộ test.
- Mỗi bộ test gồm một dòng chứa hai số nguyên X và Y — tổng số người và số người bị nhiễm thủy đậu.
OUTPUT FORMAT
- Với mỗi bộ test, in ra một số nguyên duy nhất — giá trị N nhỏ nhất thỏa mãn yêu cầu.
CONSTRAINTS
- 1 ≤ T ≤ 200
- 1 ≤ X ≤ 1000
- 0 ≤ Y ≤ X
Ví dụ
Input
3
4 0
5 3
3 3
Output
4
8
5
Giải thích:
- Test 1: Cần ít nhất 3 phòng để bố trí 1 người bệnh và 1 người khỏe: [H, B, H] (H: người khỏe, B: người bệnh).
- Test 2: Cần ít nhất 7 phòng: [H, B, H, H, H, B, H].
- Test 3: Cần ít nhất 14 phòng: [H, B, H, H, H, B, H, H, H, B, H, H, H, H].
- Test 4: Cần ít nhất 28 phòng để bố trí 5 người bệnh và 15 người khỏe theo quy tắc. ```cpp #include <iostream>
int main() { ios_base::sync_with_stdio(false); cin.tie(NULL);
int t;
cin >> t;
while (t--) {
int x, y;
cin >> x >> y;
int n;
if (y == 0) {
n = x;
} else if (y == x) {
n = 2 * y - 1;
} else {
if (y == 1) {
n = x + 2;
} else {
n = x + (y - 1) + ((x - y) + (y - 1) - 1) / (y - 1);
}
}
cout << n << endl;
}
return 0;
}
```
4
8
5
Comments