Các thao tác cơ bản với mảng kí tự
####Chào các bạn học viên đang theo dõi khóa học lập trình trực tuyến ngôn ngữ C++.
Trong bài học này, chúng ta sẽ cùng tìm hiểu một số cách để thao tác với C-style string thông qua các hàm đã được định nghĩa sẵn trong thư viện cstring. Vì có rất nhiều công việc cần giải quyết khi thao tác với mảng kí tự nên người ta đã tạo ra thư viện cstring để định nghĩa một tập hợp các hàm xử lý mảng kí tự, giúp chúng ta tiết kiệm thời gian và công sức viết code hơn trong khi chúng ta chỉ cần sử dụng lại những gì đã có sẵn.
Các bạn cần include thư viện cstring để có thể sử dụng các hàm được định nghĩa bên trong nó.
###Thiết lập giá trị cho một vùng nhớ thuộc mảng kí tự
Hàm memset sẽ giúp chúng ta lấp vào một số ô nhớ liên tiếp nhau trong mảng kí tự bằng một giá trị xác định. Dưới đây là khai báo của hàm memset:
void* memset( void *ptr, int value, size_t num);
Các bạn có thể hiểu hàm memset thiết lập num bytes ô nhớ đầu tiên bắt đầu từ địa chỉ ptr bằng giá trị value định sẵn. Ví dụ như sau:
char foo[] = "Almost every programmer should know memset!";
cout << foo << endl;
memset(foo, '-', 7);
cout << foo << endl;
Sau khi chạy đoạn chương trình này, kết quả mình nhận được trên màn hình console là 2 dòng text như sau:
Almost every programmer should know memset!
-------every programmer should know memset!
Như vậy, 7 bytes đầu tiên (tương đương với 7 kí tự trong mảng foo) đã bị hàm memset thiết lập bằng kí tự dấu trừ.
Chúng ta cũng thường sử dụng hàm memset để khởi tạo toàn bộ mảng kí tự bằng một giá trị xác định nào đó.
char foo[20];
memset(foo, 'a', sizeof(foo));
foo[sizeof(foo) - 1] = ' '; //Don't forget to set the ending character at the end of C-style string.
Với đoạn code này, mảng kí tự foo sẽ được lấp toàn bộ mảng bằng kí tự a, và kí tự cuối cùng sẽ được thiết lập là kí tự kết thúc chuỗi như mình đã trình bày trong bài học về mảng kí tự.
###Xem độ dài của chuỗi kí tự
Các bạn cần phân biệt rõ số lượng ô nhớ được cấp phát cho mảng kí tự sizeof(c_string)
và độ dài của chuỗi kí tự (length of c_string)
.
Độ dài của chuỗi kí tự được tính từ kí tự đầu tiên cho đến kí tự kết thúc chuỗi (’ ’), trong khi đó, số lượng ô nhớ được cấp phát cho mảng kí tự được trả về thông qua toán tử sizeof.
Để lấy được giá trị là độ dài của chuỗi kí tự được lưu bên trong mảng kí tự, chúng ta có thể sử dụng hàm strlen được khai báo như sau:
size_t strlen ( const char * str );
Hàm strlen nhận vào tên mảng kí tự mà các bạn muốn lấy độ dài và trả về một số nguyên là độ dài chuỗi kí tự lưu trong mảng. Ví dụ:
char foo[50] = "C++ Programming language";
cout << "Length of foo string: " << strlen(foo) << endl;
Đoạn code trên sẽ cho ra kết quả độ dài của chuỗi kí tự "C++ Programming language"
là 24. Trong khi đó, nếu các bạn dùng toán tử sizeof(foo) thì kết quả sẽ là 50, vì mình yêu cầu cấp phát 50 ô nhớ cho mảng kí tự foo
.
###Sao chép mảng kí tự
Khi sử dụng thư viện cstring, chúng ta có 2 cách để sao chép chuỗi kí tự từ một mảng kí tự sang mảng kí tự khác.
#####Sử dụng hàm strcpy
Khai báo của hàm strcpy trong thư viện cstring:
char * strcpy ( char * destination, const char * source );
Hàm strcpy cho phép bạn sao chép toàn bộ chuỗi kí tự được lưu bên trong mảng kí tự source sang mảng kí tự destination.
Ví dụ:
char str_source[] = "This is source string";
char str_destination[30];
strcpy(str_destination, str_source);
cout << "str_destination: " << str_destination << endl;
Cách hoạt động của hàm strcpy:
Lưu ý: Mảng kí tự str_destination
phải được cấp phát đủ bộ nhớ để lưu trữ chuỗi kí tự được copy từ mảng kí tự str_source
.
#####Sử dụng hàm strncpy
Hàm strncpy trong thư viện cstring được khai báo như sau:
char * strncpy ( char * destination, const char * source, size_t num );
Hàm strncpy cho phép bạn sao chép num kí tự từ mảng kí tự source sang mảng kí tự destination.
Ví dụ:
char str_source[] = "This is source string";
char str_destination[30];
strncpy(str_destination, str_source, strlen(str_source) / 2);
Với đoạn code trên, chỉ có một nữa số lượng kí tự của mảng str_source
được copy sang mảng str_destination
.
#####Sử dụng hàm strncpy_s khi sử dụng Visual studio 2015
Khi sử dụng hàm strcpy và strncpy, có một số trường hợp dẫn đến việc copy dữ liệu không an toàn. Ví dụ:
char str_source[] = "aaaaaaaaaaaaaaaaaaaaaa";
char str_destination[10];
strcpy(str_destination, str_source);
Đoạn code trên có thể gây xung đột vùng nhớ vì số lượng kí tự được copy vượt ra ngoài giới hạn vùng nhớ của str_destination
. Dó đó, Visual studio 2015 sẽ ngăn chặn hành vi build chương trình.
Vì Visual studio 2015 áp dụng chuẩn C++11 trở lên, nên chúng ta cần sử dụng hàm strncpy_s
thay thế cho 2 hàm copy mảng kí tự trong thư viện cstring.
char str_source[] = "This is source string";
char str_destination[30];
strncpy_s(str_destination, str_source, strlen(str_source));
Trong ví dụ trên, mình thực hiện copy toàn bộ mảng str_source
vào mảng str_destination
bằng cách truyền vào đối số thứ ba của hàm là độ dài toàn bộ chuỗi kí tự bên trong mảng str_source
.
Nếu gặp trường hợp số lượng ô nhớ của mảng str_destination
không đủ để chứa số lượng phần tử được copy từ mảng str_source
, Visual studio sẽ đưa ra thông báo vi phạm Assertion của hàm strncpy_s
.
###So sánh hai chuỗi kí tự
Hàm strcmp sẽ giúp chúng ta so sánh hai chuỗi kí tự.
int strcmp ( const char * str1, const char * str2 );
Khi sử dụng hàm so sánh 2 chuỗi strcmp, có 3 trường hợp có thể xảy ra:
-
Giá trị trả về là 0:
Điều này có nghĩa nội dung của hai chuỗi kí tự này hoàn toàn giống nhau. Ví dụ:
char str1[] = "This is a string"; char str2[] = "This is a string"; if(strcmp(str1, str2) == 0) { cout << "str1 and str2 are equal" << endl; } else { cout << "str1 ans str2 are not equal" << endl; }
-
Giá trị trả về nhỏ hơn 0:
Điều này có nghĩa tại vị trí phát hiện cặp kí tự không tương xứng giữa str1 và str2 tạm gọi là vị trí
index_not_match
, ta có:str1[index_not_match] < str[index_not_match]
Ví dụ:
char str1[] = "abcDEF"; char str2[] = "abcdef";
Khi so sánh chuỗi
str1
và chuỗistr2
như trên bằng dòng lệnhstrcmp(str1, str2)
, ta nhận được giá trị trả về nhỏ hơn 0, vì tại vị trí có chỉ số là 3, kí tự ‘D’ củastr1
có mã ASCII nhỏ hơn kí tự ‘d’ củastr2
. -
Giá trị trả về lớn hơn 0:
Ngược lại với việc giá trị trả về nhỏ hơn 0. Mình lấy lại ví dụ trên:
char str1[] = "abcDEF"; char str2[] = "abcdef";
Nếu các bạn thực hiện so sánh như sau:
strcmp(str2, str1);
Giá trị trả về sẽ lớn hơn 0.
###Nối chuỗi kí tự bằng một chuỗi kí tự khác
Để thực hiện thao tác nối một chuỗi bằng chuỗi kí tự khác, chúng ta sử dụng hàm strcat trong thư viện cstring:
char * strcat ( char * destination, const char * source );
Ý nghĩa của hàm này là nối vào sau chuỗi destination một bản copy của chuỗi kí tự source.
Lưu ý: Mảng kí tự destination phải có đủ bộ nhớ để chứa được thêm chuỗi mới được nối vào.
char str_destination[50] = "Hello";
strcat( str_destination, " " );
strcat( str_destination, "every" );
strcat( str_destination, "one");
cout << str_destination << endl;
Tuy nhiên, Visual studio 2015 sử dụng chuẩn C++11 trở lên, vì thế đối với môi trường làm việc là Visual studio 2015, chúng ta nên sử dụng hàm
strcat_s
.
Hàm strcat_s
có cách sử dụng hoàn toàn giống với hàm strcat
, các bạn chỉ cần đổi lại tên hàm:
char str_destination[50] = "Hello";
strcat_s( str_destination, " " );
strcat_s( str_destination, "every" );
strcat_s( str_destination, "one");
###Tìm kiếm chuỗi kí tự trong một chuỗi kí tự khác
Phần này có liên quan đến khái niệm con trỏ mà các bạn sẽ được học trong các bài học sắp tới, vì thế, mình sẽ chưa giải thích nhiều về thao tác của hàm tìm kiếm chuỗi mà thư viện cstring cung cấp.
Để thực hiện tìm kiếm chuỗi kí tự pattern
bên trong chuỗi kí tự text
nào đó, chúng ta sử dụng hàm strstr:
const char * strstr ( const char * text, const char * pattern );
Có thể tạm hiểu, hàm này sẽ trả về địa chỉ của ô nhớ của mảng kí tự text mà hàm này tìm thấy sự trùng khớp giữa chuỗi kí tự pattern với chuỗi kí tự text. Nếu không tìm thấy, hàm này trả về giá trị NULL.
Để biết được vị trí mà hàm strstr tìm thấy chuỗi pattern trong chuỗi text, các bạn có thể lấy địa chỉ của hàm strstr trả về trừ đi địa chỉ của ô nhớ đầu tiên trong mảng kí tự text.
char text[] = "This is a simple string";
char pattern[] = "simple";
char *p = strstr(text, pattern);
if (p == NULL) {
cout << "Could not find the pattern string in the text string" << endl;
}
else {
int32_t match_index = (p - text) / sizeof(char);
cout << "The pattern string match the text string at: " << match_index << endl;
}
###Tổng kết
Trong bài học này, chúng ta vừa làm quen với một số thao tác cơ bản với C-style string.
Một số hàm trong thư viện cstring khi sử dụng trong môi trường làm việc Visual studio 2015 sẽ bị cảnh báo không an toàn, và chương trình không thể biên dịch. Đối với những hàm này, chúng ta cần sử dụng theo chuẩn của Visual studio 2015, nhưng đó không phải là những hàm chuẩn, nó chỉ dùng trong môi trường của Visual studio 2015.
Thư viện cstring còn hổ trợ cho chúng ta rất nhiều những hàm khác, nhưng thường ít được sử dụng. Các bạn có thể truy cập liên kết bên dưới để tham khảo thêm về thư viện cstring:
http://www.cplusplus.com/reference/cstring/
###Bài tập cơ bản
1/ Viết chương trình nhập một chuỗi kí tự vào từ bàn phím, chuyển tất cả các kí tự trong chuỗi thành dạng in hoa. Ví dụ:
Chuỗi kí tự nhập vào: “This is a simple string”
Output: “THIS IS A SIMPLE STRING”
2/ Viết chương trình nhập vào một chuỗi kí tự str và một kí tự ch từ bàn phím, đếm trong chuỗi kí tự str có bao nhiêu lần xuất hiện kí tự ch mà bạn vừa nhập.
Hẹn gặp lại các bạn trong bài học tiếp theo trong khóa học lập trình C++ hướng thực hành.
Mọi ý kiến đóng góp hoặc thắc mắc có thể đặt câu hỏi trực tiếp tại diễn đàn.
www.daynhauhoc.com
Link Videos khóa học
C++ Cơ bản dành cho người mới học lập trình | Udemy
Bắt đầu học lâp trình bằng ngôn ngữ C++
trường hợp này là trả về địa chỉ của chuỗi pattern đầu tiên tìm thấy trong chuỗi text phải không.
giống như string.indexof();
Bài tập vui: ( áp dụng bài học so sánh chuỗi ký tự và ôn lại vòng lặp do … while, while)
Yêu cầu: Chạy chương trình cho người dùng nhập vào nick name của mình. Kiểm tra nếu người dùng nhập đúng nick name thì thoát vòng lặp, output ra 1 câu chửi nick name đó. Nếu không đúng thì tiếp tục kiểm tra 1 số điều kiện để nhắc nhở người dùng nhập cho đúng.
Giải bài của mình: (mình lấy ví dụ nick name bạn mình là “vietdexom”, các bạn áp dụng cho nick name của các bạn nhé, đem chương trình đi chọc bạn cho vui, vừa học vừa chơi :D)
P/s: Bạn nào có thể chuyển chương trình qua sử dụng do … while xem sao nhé.
Hi mọi người !
Mình muốn làm 1 chương trình trò chơi là “Oảnh tù tì”
Mình muốn sử dụng hàm lấy chuỗi ngẫu nhiên trong mảng ký tự cho trước (“bao”,“bua”,“keo”) để đem so sánh với chuỗi ký tự người dùng nhập vào.
Bạn nào biết sử dụng hàm nào để làm được việc đó. Cám ơn mọi người.
Hi mọi người, sau một vài ngày từ mình cũng tìm ra cách chọn chọn ngẫu nhiên với chuỗi cho trước với trò chơi “oản tù tì”.
Nhưng mình có một vài thắc mắc sau:
khi mình khai báo mảng ký tự char computer[10] ban đầu để lưu giá trị ngẫu nhiên của computer sinh ra, khi đến câu lệnh điều kiện để gán giá trị cho chuỗi computer theo số ngẫu nhiên sinh ra. Thì mình bắt buộc phải khai lại kiểu ký tự computer lần nữa, biến computer này mình đã khai báo từ đầu rồi.
ví dụ
Nhờ các bạn xem giúp.
Đây là đoạn code Trò chơi “Oản Tù Tì” của mình.
P/s: Mình đang dùng cloud9 ide, bạn nào biết lệnh nào dừng 1 khoảng thời gian nhưng trong visual studio 2015 không ah?
bạn chỉ gọi srand(time(NULL)) 1 lần duy nhất, ko phải gọi mỗi khi xài rand().
còn về câu hỏi của bạn thì phải xài strcpy
Bạn nào làm được rồi cho mình xem code của bài 1 với
Sao em nhập zô nó báo lỗi 6 47 C:\Documents and Settings\Administrator\Desktop\C++\thuchanhvaoday.cpp [Error] ‘strlen’ was not declared in this scope là sao? Code:
#include
using namespace std;
int main()
{
char foo[50] = “C++ Programming language”;
cout << "Length of foo string: " << strlen(foo) << endl;
return 0;
}
Bạn thiếu:
PS: Bạn làm ơn post code vào markdown dùm.
Cảm ơn thánh. Thanks so much…Ngại chết đi được
Đoạn này *p là sao hả anh ?? Có phải lưu giá trị của kết quả strstr(text, pattern); vào p không ạ ?
char *p = strstr(text, pattern);
if (p == NULL) {
cout << “Could not find the pattern string in the text string” << endl;
Đoạn này *p là sao hả anh ?? Có phải lưu giá trị của kết quả strstr(text, pattern); vào p không ạ ?
Hàm
strstr
có chức năng tìm vị trí xuất hiện đầu tiên của chuỗis2
trongs1
Nếu thành công, nó trả về con trỏ đến vị trí xuất hiện đầu tiên của
s2
trongs1
Còn thất bại thì trả về
NULL
Vậy nên nếu tìm thấy
s2
trongs1
thì có thể code:Còn
*p
là 1 con trỏ, nó trỏ đến cái vị trí xuất hiện đầu tiên củas2
trongs1
. Nếu nó không trỏ tới, tức làNULL
thì coi như không tìm thấys2
trongs1
và ngược lạiChưa học con trỏ mà -_- Thế còn toán tử & thì là sao hả anh ?
Nếu bạn chưa học con trỏ thì có thể “cưỡi ngựa xem hoa” phần này
Nhưng chưa học cũng không sao,
*p
là 1 con trỏ, nhưng trong tình huống này thì nó “không phải” là con trỏ.&
là toán tử lấy địa chỉ của 1 biến được khởi tạo ở trong bộ nhớ. Nhưng mình có thấy&
đâu nhỉ ?@@ Dù sao cũng cám ơn
mn xem giúp mình bài 2 mình làm sai ở đâu với
thiế dấu = thành phép gán kìa bạn
minh dùng VS 2015 khi viết code này:
nó báo lỗi là :
c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\string.h(119): note: see declaration of 'strcpy'
mình không biết nó bị sai chỗ nào? mong mọi người giúp đỡ.
bạn để dòng
#pragma disable(warning:4996)
lên đầu file coi được không. Cái lỗi nhìn hơi lạ lạ =))không được luôn bạn ơi, nó cũng bị lỗi y chan như vậy.
Sau một hồi tìm kiếm trên mạng, người ta nói không nên sử dụng ‘strcpy’ vì nó không an toàn ('có khả năng bị lỗi dẫn đến tràn chỗ chứa) và khuyên chuyển sang dùng ‘strcpy_s’