Cùng với mảng một chiều và hai chiều, xâu trong lập trình màng một ý nghĩa hết sức to lớn, hầu hết các ứng dụng chúng ta thấy hằng ngày đều có sự xuất hiện các dòng các đoạn và thậm chí là hàng trang văn bản.
Tất cả các ngôn ngữ lập trình đều cung cấp cho chúng ta cách thức định nghĩa và sử dụng dữ liệu xâu. Trong C++ cung cấp hai cách thức làm việc với loại dữ liệu này:
Mảng ký tự
Lớp string
Các xâu trong C++ là một dãy kí tự chấm dứt bằng kí tự rỗng '\0'
Cách khai báo này giống hệt khái báo mảng một chiều với kiểu là char
ví dụ
char s[50]; //Khai báo một xâu s có thể chứa tối đa 50 kí tự bao gồm cả kí tự trống
Các em lưu ý rằng để khai báo biến xâu chứa một chuỗi văn bản mà các em cần lưu trữ, các em phải khai báo xâu có độ dài lớn hơn độ dài chuỗi của mình ít nhất một kí tự.
Ví dụ chúng ta muốn khai báo một xâu chứa từ "Hello", phải khai báo xâu có ít nhất 6 kí tự;
char greeting[ 6] = { 'H' , 'e' , 'l' , 'l' , 'o' , ' \0' };
Hoặc viết như sau:
char greeting[] = "Hello";
Cả hai cách trên đều tạo ra một xâu như sau:
Mặc dù khai báo và khởi tạo xâu giống mảng một chiều, nhưng cách nhập xuất xâu lại đơn giản hơn nhiều.
Để nhập xâu các em dùng gets hoặc getline, các em quan sát ví dụ sau đây:
#include <iostream>
using namespace std;
int main(){
char s[50];
cout<<"nhap xau s:";
cin.getline(s,50);
cout<<"xau ban vua nhap la:";
cout<<s;
return 0;
system("pause");
}
Tại dòng 6 các em thấy thầy dùng lện cin.getline để nhập xâu, số 50 phía sau s chỉ độ dài mà s chứa được, số này phải bằng số mà các em khai báo s ban đâu.
hoặc ví dụ sau thầy dùng gets:
#include <iostream>
using namespace std;
int main(){
char s[50];
cout<<"nhap xau s:";
gets(s);
cout<<"xau ban vua nhap la:";
cout<<s;
return 0;
system("pause");
}
Các em hoàn toàn có thể dùng lệnh cin để nhập xâu bình thường, với điều kiện xâu này không chứa khoảng trắng, nếu có khoảng trắng thì xâu chỉ nhận được từ đầu tiên. Ví dụ với chương trình trên mà thầy viết:
cout<<"nhap xau s:";
cin>>s;
cout<<"xau ban vua nhap la:";
cout<<s;
Thì khi chạy chương trình, nếu nhập "chao cac ban", kết quả xuất ra sẽ là "chao".
Một điều cần lưu ý khi làm việc với lệnh gets và getline là nhiều khi nó lấy các giá trị rác trong vùng đệm đưa vào xâu, lúc đó có khả năng xâu nhận giá trị sai hoặc nhận giá trị enter trong vùng đệm do lệnh nhập trước đó gây ra sẽ bỏ qua bước nhập xâu và thực hiện bước tiếp theo, lúc này các em không thể nhập xâu được, người ta hay gọi hiện tượng này là hiện tượng trôi lệnh. Để khắc phục việc này, trước mỗi lệnh gets và getline các em đặt lệnh fflush(stdin) để xóa hết rác trong vùng đệm đi.
nghĩa là như thế này:
#include <iostream>
using namespace std;
int main(){
char s[50];
cout<<"nhap xau s:";
fflush(stdin);// xóa vùng đệm
cin.getline(s,50);
cout<<"xau ban vua nhap la:";
cout<<s;
return 0;
system("pause");
}
Các thao tác trên xâu cũng là những ừng dụng phổ biến nhất khi chúng ta dùng các ứng dụng để để làm việc với văn bản, các thao tác này thường là:
- So sánh xâu
- Cắt xâu
- Ghép xâu
- Sao chép xâu
- Lấy độ dài xâu
- In hoa, in thường
- Chèn xâu
......
Sau đây là một vài hàm thông dụng:
Ví dụ sau đây sử dụng các hàm trên:
#include <iostream>
#include <cstring>
using namespace std;
int main ()
{
char str1[ 10] = "Hello";
char str2[ 10] = "World";
char str3[ 10];
int len ;
// copy str1 into str3
strcpy( str3, str1);
cout << "strcpy( str3, str1) : " << str3 << endl;
// concatenates str1 and str2
strcat( str1, str2);
cout << "strcat( str1, str2): " << str1 << endl;
// total lenghth of str1 after concatenation
len = strlen(str1);
cout << "strlen(str1) : " << len << endl;
return 0;
}
chạy chương trình trên kết quả như sau:
strcpy( str3, str1) : Hello
strcat( str1, str2): HelloWorld
strlen(str1) : 10
Để dùng lớp này các em khai báo thêm thư viên string
Khi đã dùng đến thư viện này, các thao tác nhập xuất giống với dùng mảng kí tự, nhưng một số thao tác trên xâu lại đơn giản hóa hơn rất nhiều, cách thực hiện các thao tác của xâu cũng tường minh và dễ hiểu hơn nhiều. Các em quan sát ví dụ sau đây để thấy cách sử dụng lớp string này trong C++ nhé.
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str1 = "Hello";
string str2 = "World";
string str3;
int len ;
// copy str1 into str3
str3 = str1;
cout << "str3 : " << str3 << endl;
// concatenates str1 and str2
str3 = str1 + str2;
cout << "str1 + str2 : " << str3 << endl;
// total lenghth of str3 after concatenation
len = str3. size();
cout << "str3.size() : " << len << endl;
return 0;
}
Chạy chương trình trên kết quả như sau:
str3 : Hello
str1 + str2 : HelloWorld
str3. size() : 10
- Mỗi ký tự của xâu còn gọi là một phần tử của xâu.
- Mỗi ký tự có một chỉ số (được đánh bắt đầu từ 0).
- Để Truy vuất vào một phần tử nào đó, ta dựa vào chỉ số của nó.
Ví dụ:
Với xâu Str3 = "Hello" ở trên,
Str3[0] = 'H'
Str3[1] = 'e'
....
Thao tác nhập xâu với lớp String đơn giản và hiệu quả nhất là dùng hàm getline.
Cú pháp hàm này như sau:
getline(cin, tên xâu);
Ví dụ:
// extract to string#include <iostream>#include <string>int main () { std::string name; std::cout << "Please, enter your full name: "; std::getline (std::cin,name); std::cout << "Hello, " << name << "!\n"; return 0; }
Chạy chương trình trên các em sẽ thấy kết quả như sau:
Please, enter your full name: chao ban Hello, chao ban!
Cách lấy độ dài của một xâu trong lớp String chúng ta dùng hàm length()
Cú pháp: <Tên xâu>.length();
Ví dụ
// string::length#include <iostream>#include <string> using namespace std; int main () { string str ("Test string"); cout << "The size of str is " << str.length() << " bytes.\n"; return 0; }
Chạy chương trình được kết quả như sau:
The size of str is 11 bytes
Là thao tác ghép hai xâu lại với nhau thành một xâu dài hơn. Trong C++ chúng ta có thể dùng toán tử '+' để thực hiện điều này.
Ví dụ:
#include <iostream>#include <string> using namespace std; int main () { string S1; string S2, S3;
S1 = "Duong";
S2 = "Ngo Quyen";
S3 = S1 + S2;
cout<<S3; return 0;
}
Chạy chương trình kết quả ra màn hình là:
DuongNgo Quyen
Bài tập nhỏ 1:
Em hãy sửa lại chương trình trên một chút để kết quả S3 là "Duong Ngo Quyen"
Bài tập nhỏ 2:
Em hãy viết chương cho phép nhập vào hai xâu S1, S2. Xâu S3 = S1+S2. Xuất ra S3 và độ dài của S3 trên 2 dòng khác nhau.
là thao tác lấy một đoạn nào đó trong một xâu lớn để gán cho xâu khác.
Đối với lớp String trong C++, thao tác này thực hiện thông qua hàm substr(vị trí, số ký tự)
Cú Pháp: <xâu con> = <tên xâu mẹ>.substr( vị trí bắt đầu lấy, số ký tự muốn lấy);
Ví dụ:
// string::substr
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str="We think in generalities, but we live in details.";
string str2 = str.substr (3,5); // "think"
cout << str2 << '\n';
return 0;
}
Ví dụ trên copy đoạn 5 ký tự bắt đầu từ ký tự thứ 3 trong xâu str rồi gán vào cho str2.
Bài tập nhỏ 3:
khai báo thêm một xâu tên str3, dùng lệnh substr() lấy chữ "generalities" của str gán cho str3, xuất str3 ra màn hình.
Chèn một xâu này vào xâu khác tại một vị trí chỉ định nào đó.
Đối với lớp String của C++, thao tác này được thực hiện thông qua thủ tục insert()
Cú pháp: <Tên xâu mẹ>.insert( vị trí bắt đầu chèn, tên xâu con);
ý nghĩa: Chèn xâu con vào vị trí bắt đầu chèn trong xâu mẹ.
Ví dụ:
// inserting into a string#include <iostream>#include <string> using namespace std; int main () { string str="to be question"; string str2="the "; string str3="or not to be"; // used in the same order as described above: str.insert(6,str2); // to be (the )question str.insert(6,str3,3,4); // to be (not )the question str.insert(10,"that is cool",8); // to be not (that is )the question str.insert(10,"to be "); // to be not (to be )that is the question str.insert(15,1,':'); // to be not to be(:) that is the question it = str.insert(str.begin()+5,','); // to be(,) not to be: that is the question str.insert (str.end(),3,'.'); // to be, not to be: that is the question(...) str.insert (it+2,str3.begin(),str3.begin()+3); // (or ) std::cout << str << '\n'; return 0; }
Chạy chương trình trên cho kết quả như sau:
to be, or not to be: that is the question...
Bài tập nhỏ 4:
Viết chương trình gán xâu S1 = "Hung Vuong"; xâu S2 = "Duong". Hãy gán S2 vào S1 sao cho S1 trở thành "Duong Hung Vuong"
Là thao tác xóa bớt một số ký tự trong xâu cho trước. Trong C++, thao tác này thực hiện bằng thủ tục erase.
Cú pháp: <Tên xâu>.erase(vị trí bắt đầu xóa, số ký tự muốn xóa)
Ví dụ:
// string::erase#include <iostream>#include <string> using namespace std; int main () { string str ("This is an example sentence."); cout << str << '\n'; // "This is an example sentence." str.erase (10,8); // ^^^^^^^^ cout << str << '\n'; // "This is an sentence." str.erase (str.begin()+9); // ^ cout << str << '\n'; // "This is a sentence." str.erase (str.begin()+5, str.end()-9); // ^^^^^ cout << str << '\n'; // "This sentence." return 0; }
Chạy chương trình trên ta được output như sau:
This is an example sentence. This is an sentence. This is a sentence. This sentence.
Bài tập nhỏ 5
Viết chương trình gán xâu S3 = "Duong An Duong Vuong";
Thực hiện lện xóa để biến S3 thành "Duong An Vuong". Xuất S3 ra màn hình.
Nếu muốn tìm xem một xâu nào đó xuất hiện trong xâu lớn ở vị trí đầu tiên nào, C++ cung cấp cho ta hàm find.
Cú pháp: Vị trí = <tên xâu lớn>.find( tên xâu nhỏ);
Trongđó Vị trí là một biến kiểu nguyên.
Ví dụ:
int main ()
{
string str ("There are two needles in this haystack with needles.");
string str2 ("needle");
int found;
// different member versions of find in the same order as above:
found = str.find(str2);
if (found!=string::npos)
cout << "first 'needle' found at: " << found << '\n';
found=str.find("needles are small",found+1,6);
if (found!=string::npos)
cout << "second 'needle' found at: " << found << '\n';
found=str.find("haystack");
if (found!=string::npos)
cout << "'haystack' also found at: " << found << '\n';
found=str.find('.');
if (found!=string::npos)
cout << "Period found at: " << found << '\n';
// let's replace the first needle:
str.replace(str.find(str2),str2.length(),"preposition");
std::cout << str << '\n';
return 0;
}
Giải thích:
- string::npos: là vị trí không tồn tại, hoặc vị trí ở một ô nhớ rất xa trong bộ nhớ, ý nghĩa việc so sánh này là nếu tìm được vị trí của xâu con trong xâu mẹ.
Chạy chương trình trên cho ta kết quả:
first 'needle' found at: 14 second 'needle' found at: 44 'haystack' also found at: 30 Period found at: 51
There are two prepositions in this haystack with needles.
Bài tập nhỏ 6:
Viết một chương trình ngắn cho phép người dùng nhập vào hai xâu S1, S2. Tìm vị trí của S2 trong S1 rồi xuất vị trí này ra màn hình. Nếu không tìm được thì xuất ra -1.
Ví dụ:
Input
Duong Nguyen Tri Phuong
Tr
Output
13
Ví dụ 2:
Input
Duong Hung Vuong
An
Output
-1
Rất nhiều hàm và thủ tục khác của lớp string rat61 hay, các em xem thêm tại đây:
http://pages.cs.wisc.edu/~cs368-2/CppTutorial/NOTES/STRING.html
hoặc:
http://www.cplusplus.com/reference/string/string/
Tuy nhiên, rất nhiều thao tác trên xâu không thể xụng hàm nào đó, mà phải kết hợp nhiều hàm, hoặc phải dùng vòng lặp truy xuất vào từng phần tử của xâu để làm việc.
Ví dụ:
- Nhập vào xâu rồi xuất ra xâu đó theo thứ tự ngược lại
- Nhập vào một xâu rồi in hoa toàn bộ xâu đó
- Nhập vào một xâu rồi kiểm tra xâu có đối xứng hay không
- Nhập vào một xâu rồi đếm xem xâu đó có bao nhiêu từ.
....
Để hiểu rõ hơn về các dạng bài tập này, các em truy cập trang tkncoder.net --> mở danh sách bài tập --> mở tìm kiếm --> tại ô mã bài nhập K11_C5 --> bấm tìm kiếm.
Thực hiện lần lược các bài tập trong danh sách hiện ra (14 bài)
Truyền xâu vào hàm tương tự như mảng
Các em quan sát hai ví dụ sau, hai ví dụ đều viết hàm để xuất xâu, ví dụ đầu thầy dùng mảng kí tự, ví dụ sau dùng lớp string.
Truyền xâu vào hàm bằng mảng kí tự
#include <iostream>
using namespace std;
void xuatxau(char s[]){
cout<<"xau la:"<<s;
}
int main(){
char a[20];
cout<<"nhap xau a:";
cin.getline(a,20);
xuatxau(a);
return 0;
system("pause");
}
Truyền xâu vào hàm bằng lớp string
#include <iostream>
#include <string>
using namespace std;
void xuatxau(string s){
cout<<"xau la:"<<s;
}
int main(){
string a;
cout<<"nhap xau a:";
fflush(stdin); // xoa vung dem
getline(cin,a,'\n');// dau '\n' bao hieu nhap xau den khi gap dau enter
xuatxau(a);
return 0;
system("pause");
}