Xâu trong C++
Bài viết sưu tầm trên mạng.
0. Kiểu chuỗi của C và hạn chế¶
Khi mới học C, chắc các bạn đều rất bối rối khi làm việc với xâu ký tự, việc sử dụng con trỏ lưu xâu ký tự rất phức tạp, dễ gây lỗi khiến nhiều người cho rằng nó không bằng xâu ký tự trong Pascal.
Các chương trình C++ có thể sử dụng chuỗi theo cách thức cũ của Ngôn ngữ C (trong bài viết này gọi là C-String): mảng các ký tự kết thúc bởi ký tự mã ASCII là 0 (ký tự \0) cùng với các hàm thư viện khai báo trong <string.h>. Có nhiều bất tiện khi dùng theo cách thức này:
- Người lập trình phải chủ động kiểm soát bộ nhớ cấp phát cho chuỗi ký tự. Nói chung là phải am hiểu và rất thông thạo về kỹ thuật dùng bộ nhớ và con trỏ thì chương trình mới tránh được các lỗi về kỹ thuật;
- Không thể gán giá trị hay sử dụng phép toán
+(ghép chuỗi) và các phép toán so sánh như:>(lớn hơn),<(nhỏ hơn),... mà phải gọi các hàm thư viện trong<string.h>; - Nếu dùng kỹ thuật cấp phát động thì phải quản lý việc cấp thêm bộ nhớ khi chuỗi dãn ra (chẳng hạn do ghép chuỗi) và phải hủy bộ nhớ (khi không dùng nữa) để tránh việc cạn kiệt bộ nhớ của máy tính trong trường hợp có nhiều chương trình hoạt động đồng thời.
1. Kiểu chuỗi string trong thư viện STL của C++¶
Thư viện chuẩn STL (Standard Template Library) cung cấp kiểu string (xâu ký tự), giúp các bạn tránh khỏi hoàn toàn các phiền phức nêu trên.Các chỉ thị #include cần khai báo để sử dụng string:
Nếu bạn muốn tận dụng các hàm của C-String, cần chuyển đổi giữa 2 kiểu dữ liệu này:
- Chuyển từ
stringsang C-String:
- Chuyển từ C-String sang
string:
2. Các phương thức, phép toán tiện ích của kiểu string¶
Kiểu string của STL hỗ trợ các nhóm phương thức và phép toán tiện ích sau đây.
a) Các phép toán và phương thức cơ bản¶
- Các toán tử
+,+=dùng để ghép hai chuỗi và cũng để ghép một ký tự vào chuỗi; - Các phép so sánh theo thứ tự từ điển:
==(bằng nhau),!=(khác nhau),>(lớn hơn),>=(lớn hơn hay bằng),<(nhỏ hơn),<=(nhỏ hơn hay bằng); - Hàm
length()và phép lấy chỉ số[]để duyệt từng ký tự của chuỗi: nếuslà biến kiểustringthìs[i]là ký tự thứicủasvới \(0 \le i < s.length()\); -
Phép gán
=dùng để gán biến kiểustringbằng một chuỗi, hoặc bằngstringkhác, chẳng hạn:string s="ABCDEF"; hays1=s2; mà không cần copy xâu. -
Những constructor thường sử dụng nhất:
- Có thể dùng toán tử
<<vớicoutđể xuất một chuỗi ra màn hình hoặc dùng toán tử>>vớicinđể nhập một chuỗi ký tự đến khi gặp một khoảng trống thì dừng.
- Một vấn đề thường nảy sinh trong các ứng dụng có sử dụng C-string: một C-String chưa khởi tạo cần được gán NULL. Tuy nhiên, rất nhiều hàm thư viện của C-String sẽ gặp sự cố trong thời gian chạy khi gặp đối tượng C-String là NULL. Chẳng hạn, lệnh
được một số trình biên dịch chấp nhận, nhưng với nhiều hiện thực khác của thư viện C-String, thì gặp lỗi trong thời gian chạy. string không gặp vấn đề này, ta hoàn toàn có thể cho 1 xâu là rỗng mà không gặp bất cứ lỗi nào: string s="";
String thực chất là một vector<char> có bổ sung thêm một số hàm và thuộc tính, do đó, nó có toàn bộ các tính chất của 1 vector, như hàm size(), push_back(), toán tử [], ...
- Các hàm từ
vector: v.size(): Số lượng phần tửv.empty(): Trả về 1 nếu chuỗi rỗng, 0 nếu ngược lại.v.max_size(): Trả về số lượng phần tử tối đa đã được cấp phátv1 == v2: Trả về 1 nếu hai chuỗi giống nhauv1 != v2: Trả về 1 nếu hai chuỗi khác nhauv.begin(): Trả về iterator đầu tiên của chuỗiv.end(): Trả về iterator cuối cùng của chuỗi (trỏ vào sau kí tự cuối cùng)v.front(): Trả về phần tử đầu tiên của chuỗiv.back(): Trả về phần tử cuối cùng của chuỗiv1.swap(v2): Hoán đổi 2 chuỗi với nhau (giống việc hoán đổi giá trị của 2 biến)
- Nhập một
stringtrên 1 dòng (chú ýcinsẽ chỉ đọc đến dấu cách hoặc xuống dòng đầu tiên):istream& getline ( istream& in, string& str, char delimiter = ‘\n’);
Đọc 1 dòng văn bản từ istream in (có thể là file hay đối tượng chuẩn cin) từng ký tự đến khi ký tự delimiter được nhập vào (mặc định là \n)
b) Các phương thức chèn, xóa, lấy chuỗi con:¶
- Phương thức
substr(int pos, int nchar)trích ra chuỗi con của một chuỗi cho trước, ví dụstr.substr(2,4)trả về chuỗi con gồm 4 ký tự của chuỗistrkể từ ký tự ở vị trí thứ 2 (ký tự đầu tiên của chuỗi ở vị trí 0).
- Phương thức
insert()chèn thêm ký tự hay chuỗi vào một vị trí nào đó của chuỗistrcho trước. Có nhiều cách dùng phương thức này: str.insert(int pos, char* s); chèns(mảng ký tự kết thúc\0) vào vị tríposcủastr;str.insert(int pos, string s); chèn chuỗis(kiểustring) vào vị tríposcủa chuỗistr;str.insert(int pos, int n, int ch); chènnlần ký tựchvào vị tríposcủa chuỗistr;
- Phương thức
str.erase(int pos, int n)xóanký tự của chuỗistrkể từ vị trípos; nếu không quy định giá trịnthì tất cả các ký tự củastrtừ vị trípostrở đi sẽ bị xóa
c) So sánh¶
Bạn có thể đơn giản là sử dụng những toán tử quan hệ (==, !=, <, <=, >=) được định nghĩa sẵn. Tuy nhiên, nếu muốn so sánh một phần của một chuỗi thì sẽ cần sử dụng phương thức compare():
Hàm trả về 0 khi hai chuỗi bằng nhau và lớn hơn hoặc nhỏ hơn 0 cho trường hợp khác Ví dụ:
d) Các phương thức tìm kiếm và thay thế¶
- Phương thức
find()tìm kiếm xem một ký tự hay một chuỗi nào đó có xuất hiện trong một chuỗistrcho trước hay không. Có nhiều cách dùng phương thức này:
Nếu không quy định giá trị pos thì hiểu mặc nhiên là 0; nếu tìm có thì phương thức trả về vị trí xuất hiện đầu tiên, ngược lại trả về giá trị -1.
- Hàm tìm kiếm ngược (
rfind)
- Phương thức
replace()thay thế một đoạn con trong chuỗistrcho trước (đoạn con kể từ một vị tríposvà đếm tớincharký tự ký tự về phía cuối chuỗi) bởi một chuỗisnào đó, hoặc bởinký tựchnào đó. Có nhiều cách dùng, thứ tự tham số như sau:
e) Tách xâu¶
Trong việc xử lý xâu ký tự, không thể thiếu được các thao tác tách xâu ký tự thành nhiều xâu ký tự con thông qua các ký tự ngăn cách. Các hàm này có sẵn trong các ngôn ngữ khác như Visual Basic, Java, hay thậm chí là trong <string.h>. Với STL, các bạn có thể dễ dàng làm điều này với stringstream:
Output:
Chú ý rằng, cách này cũng có thể dễ áp dụng nếu bạn muốn chuyển số thành xâu (hoặc ngược lại), tách 1 xâu thành nhiều số.
Nếu không muốn sử dụng stringstream, các bạn cũng có thể tự xây dựng hàm tách xâu như sau:
Output:
Đoạn chương tr.nh sử dụng các kỹ thuật sau
- Phương thức
find(vị_trí_đầu, vị_trí_cuối, ký_tự_tìm)dùng để tìm vị trí đầu tiên củaký_tự_tìmbắt đầu từvị_trí_đầu. Hàm này trả về vị trí của ký tự tìm được (nếu tìm thấy) hoặcvị_trí_cuối(nếu không tìm thấy) stringcó thể khởi tạo từ một đoạn ký tự con của một xâu ký tự khác với cú phápstring(vị_trí_đầu, vị_trí_cuối)- Đoạn chương trình thực hiện tách các xâu ký tự kể cả trong trường hợp có nhiều ký tự
spacenằm liên tiếp nhau. Một cách đơn giản hơn là bạn có thể gọi hàmstrtok()trongstring.hđể làm việc này, nhưng không may là hàm này thao tác trênchar*chứ không phảistring. Hàm thành viênc_str()sẽ giúp bạn chuyển từstringthành dạngconst charT* c_str () const;
Hàm này cũng tự động sinh ra ký tự null chèn vào cuối xâu.
Từ prototype ta cũng thấy được hàm trả về một hằng chuỗi, điều này đồng nghĩa với việc ta không thể thay đổi chuỗi trả về. Gọi phương thức c_str();
Sau đây là ví dụ bên trên được viết lại dùng hàm thành viên c_str() và các hàm trong <string.h>
Output:
f) Chuyển đổi hàng loạt với transform¶
g) Một số phương thức khác¶
Còn nhiều phương thức tiện ích khác như: append(), rfind(), find_first_not_of(), find_last_not_of(), swap(). Cách dùng các hàm này đều được trình bày trong hệ thống hướng dẫn (help) của các môi trường có hỗ trợ STL (trong VC++ là MSDN). Ngoài ra các phương thức như find_first_of() tương tự như find(), find_last_of() tương tự như rfind()