Bài giảng Kỹ thuật lập trình - Chương 5: Chuỗi Ký Tự (String) - Trịnh Tấn Đạt

Chuỗi ky tự trong C ▪ Chuỗi ký tự là mảng một chiều, có mỗi thành phần là một ký tự (hoặc có thể xem là số nguyên), được kết thúc bởi ký tự đặc biệt \0 ▪ Ký tự kết thúc (\0) ở cuối chuỗi ký tự thường được gọi là ký tự null (không giống con trỏ NULL). ▪ Được khai báo và truyền tham số như mảng một chiều Ví dụ: char s[100]; // chuỗi s chứa tối đa 100 ký tự char first_name[5] = { 'J', 'o', 'h', 'n', '\0' }; char last_name[6] = "Minor"; char other[] = "Tony Blurt"; char characters[8] = "No null"; Chuỗi ky tự trong C ❖ Nhập/ xuât chuỗi ký tự ▪ Có thể dùng scanf() hoặc cin>> để nhập chuỗi ký tự (không bao gồm khoảng trắng) o toán tử >> với cin để nhập một chuỗi ký tự đến khi gặp một khoảng trống thì dừng.

pdf62 trang | Chia sẻ: thanhle95 | Lượt xem: 656 | Lượt tải: 1download
Bạn đang xem trước 20 trang tài liệu Bài giảng Kỹ thuật lập trình - Chương 5: Chuỗi Ký Tự (String) - Trịnh Tấn Đạt, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Chuỗi Ký Tự (String) Trịnh Tấn Đạt Khoa CNTT - Đại Học Sài Gòn Email: trinhtandat@sgu.edu.vn Website: https://sites.google.com/site/ttdat88/ Nội dung ▪ Khái niệm chuỗi ký tự (string) ▪ Chuỗi ký tự trong C o Khai báo o Nhập/xuất chuỗi ký tự o Con trỏ và chuỗi ký tự o Các thao tác trên chuỗi ký tự o String và FILE o Ví dụ ▪ Câu hỏi và Bài Tập ▪ Chuỗi ký tự trong C++ (option) Ký tự (character) ▪ Kiểu char: o Ký tự “in được” gồm: 26 chữ thường (a..z), 26 chữ hoa (A..Z), 10 chữ số (0..9), khoảng trắng, các ký tự: ! “ # $ % & ‘ ( ) * + , - . / : ; ? @ [ \ ] ^ _ { | } ~ o Các ký tự “không in được”: tab, lert (bell), newline, ▪ Các ký tự “in được” đặc biệt: ‘\\’, ‘\’’, ‘\”’ ▪ Các ký tự “không in được” đặc biệt: \n new line \a bell \0 null character \b backspace \t horizontal tab Ký tự (character) ▪ Nhập xuất ký tự Trong C : char ch; scanf("%c", &ch); printf("ky tu la : %c", ch); Trong C++ : char c; cin>> c; cout<<"ky tu la:" << c; Chuỗi ky tự trong C ▪ Chuỗi ký tự là mảng một chiều, có mỗi thành phần là một ký tự (hoặc có thể xem là số nguyên), được kết thúc bởi ký tự đặc biệt \0 ▪ Ký tự kết thúc (\0) ở cuối chuỗi ký tự thường được gọi là ký tự null (không giống con trỏ NULL). ▪ Được khai báo và truyền tham số như mảng một chiều Ví dụ: char s[100]; // chuỗi s chứa tối đa 100 ký tự char first_name[5] = { 'J', 'o', 'h', 'n', '\0' }; char last_name[6] = "Minor"; char other[] = "Tony Blurt"; char characters[8] = "No null"; 'J' 'o' 'h' 'n' \0first_name Chuỗi first_name có chiều dài là 4 (không tính ký tự \0) 'T' 'o' 'n' 'y' 32 'B' 'l' 'u' 'r' 't' '\0'other Chuỗi other có chiều dài là 10 (không tính ký tự \0) Ví dụ: In ra màn hình chuỗi other: Dùng C: printf("%s",other); Dùng C++: cout<<other; • Lưu ý: - Ký tự đặt trong dấu nháy đơn - Chuỗi ký tự đặt trong dấu nháy kép Chuỗi ky tự trong C ❖ Nhập/ xuât chuỗi ký tự ▪ Có thể dùng scanf() hoặc cin>> để nhập chuỗi ký tự (không bao gồm khoảng trắng) o toán tử >> với cin để nhập một chuỗi ký tự đến khi gặp một khoảng trống thì dừng. ▪ Ví dụ: #pragma warning(disable:4996) #include #include #include using namespace std; int main() { char s[100], t[100]; cout <<"nhap chuoi:"; scanf("%s",&s); printf("%s\n",s); fflush(stdin); // xoa vung dem cout <<"nhap chuoi:"; cin >> t ; cout << t << endl; return 0; } Nhập chuỗi có khoảng trắng, nếu không dùng fflush(stdin) → kết quả là gi? Chuỗi ký tự trong C ❖ Nhập/ xuât chuỗi ký tự ▪ Nhập chuỗi có khoảng trắng dùng hàm gets() ▪ Lưu ý không thể dùng hàm gets() trong visual studio version 15 trở đi. Trước khi ra đời chuẩn C++11, chúng ta thường sử dụng hàm gets để nhập dữ liệu cho mảng kí tự. Nhưng Visual studio 2015 áp dụng chuẩn C++11 trở lên, nên hàm gets() bây giờ đổi tên thành gets_s(). ▪ Ví dụ: #include #include #include using namespace std; int main() { char name[100]; cout<<"Nhap mot chuoi ky tu: "; gets(name); cout <<"chuoi la:" << name <endl; return 0; } Ví dụ: #include #include #include using namespace std; int main() { char A[255],B[255]; cout<<"Nhap chuoi: "; gets(A); int Dodai = strlen(A); // tìm độ dài của chuỗi cout<<"Co do_dai: " << Dodai; strcpy(B,A); // copy chuỗi A sang chuỗi B cout<<"Output: "; puts(B); return 0; } String ▪ Có thể dùng hàm puts() để in chuỗi ký tự ra màn hình String ▪ Lưu ý: kết thúc chuỗi #include #include #include using namespace std; int main() { char other[] = "Tony Blurt"; cout << other << endl; other[4] = '\0'; cout << other << endl; return 0; } "Blurt" sẽ không được in ra 'T' 'o' 'n' 'y' 32 'B' 'l' 'u' 'r' 't' '\0'other String trong C ❖ Lưu ý : Lỗi khi tạo một chuỗi ▪ Đối với chuỗi ký tự trong C . Chú ý: không có phép gán trong kiểu dữ liệu chuỗi như thế này là sai ▪ Đối với chuỗi ký tự trong C . Không : sử dụng toán tử gán = để chép nội dung của một chuỗi sang chuỗi khác. ▪ Đối với chuỗi ký tự trong C . Không: dùng toán tử ==, , = để so sánh nội dung hai chuỗi char ten[10]; ten = “hoahong”; char a[4]=“hi”; char b[4]; b = a; //??? char a[] = “hi”; char b[] = “there”; if(a==b) //??? { } * Khắc phục: sử dụng các hàm trong thư viện Con trỏ và String ❖ Khai báo chuỗi ký tự như một con trỏ : char *p; ▪ Cấp phát động cho con trỏ: p = new char [30]; ▪ Xin cấp phát số lượng ký tự, giống mảng các ký tự #include #include #include using namespace std; int main() { char *p = new char[50]; cout <<"nhap chuoi:"; gets(p); cout << "chuoi vua nhap:"<< p << endl; return 0; } Con trỏ và String ❑ Lưu ý : ▪ Với một chuỗi ký tự char *s, cú pháp s+k sẽ trả về chuỗi tính từ ký tự thứ k trở về cuối chuỗi s. ▪ Với một chuỗi ký tự char *s, cú pháp s[k]= '\0' sẽ trả về chuỗi s trong đó s kết thúc tại vị trí k. 'T' 'o' 'n' 'y' 32 'B' 'l' 'u' 'r' 't' '\0' s s+3 // k =3 'T' 'o' 'n' '\0' 32 'B' 'l' 'u' 'r' 't' '\0' s[3]= '\0‘ Khi đó: s là “Ton” Con trỏ và String ▪ Xét ví dụ khởi tạo bằng một hằng chuỗi kí tự như sau: char s[] = "Lap Trinh"; s[1] = 'A'; //=> "LAp Trinh“ ▪ Chuỗi kí tự "Lap Trinh" được xem như là một chuỗi hằng kí tự, nó có địa chỉ cụ thể trên bộ nhớ ảo, nó được lưu trên bộ nhớ ảo. ▪ Khi sử dụng chuỗi hằng kí tự "Lap Trinh" để khởi tạo cho mảng s, mảng s không được khai báo là kiểu chuỗi hằng kí tự (const char []) nên các kí tự trong mảng s hoàn toàn có thể bị thay đổi. ▪ Điều này chứng tỏ mảng s được cấp phát bộ nhớ tại địa chỉ khác chuỗi hằng kí tự "Lap Trinh“, việc khởi tạo mảng kí tự bằng một chuỗi hằng kí tự chỉ đơn giản là copy từng kí tự của chuỗi "Lap Trinh" và đưa vào mảng. ▪ Do đó, con trỏ kiểu char (char *) trỏ đến mảng s và trỏ đến vùng nhớ của chuỗi hằng kí tự "Lap Trinh" là 2 trường hợp khác nhau. char s[] = "Lap Trinh"; cout << s << endl; cout << &s << endl; char *p_s = s; p_s[1] = 'A'; //=> "LAp Trinh" cout << p_s << endl; cout << &p_s << endl; cout << s << endl; cout << &s << endl; Con trỏ và String ▪ Câu hỏi: cho biết kết quả in ra màn hình char ch = 'A'; cout << ch << endl; cout << &ch << endl; Vì &ch trả về dữ liệu kiểu (char *) nên đối tượng cout xem nó như là C-style string nên in ra kí tự A và tiếp tục cho đến khi gặp giá trị '\0'. Mảng các chuỗi ▪ Dùng con trỏ cấp 2 để biểu diễn mảng các chuỗi ký tự ▪ Ví dụ char *sports[ ] = {“golf”, “hockey”, “football”, “cricket”, ”shooting”}; Hay: char **sports; Mảng các chuỗi ▪ Ví dụ: nhập vào 3 chuỗi và in ra màn hình dùng array of strings (pointers) #include #include #include using namespace std; int main() { char **s; // array of strings, cấp phát động s = new char*[3]; // cap phhat dong cho 3 chuoi // moi chuoi chua toi da 1024 ky tu for(int i=0;i<3;i++) s[i] = new char[1024]; // nhap cac chuoi trong array of strings for(int i=0;i<3;i++) { cout << "nhap chuoi:"; gets(s[i]); } // in cac chuoi ra man hinh for(int i=0;i<3;i++) { cout << "chuoi " << i+1 << "la:"; cout << s[i] << endl; } // don dep vung nho for(int i=0;i<3;i++) delete[] s[i]; delete[] s; return 0; } Có thể dùng cấp phát tỉnh: char s[100][1024]; Các hàm xử lý chuỗi trong C ▪ Các hàm xử lý chuỗi thông dụng: trong thư viện strlen(); toupper(); strupr(); strlwr(); strcpy(); strncpy(); strchr(); strstr(); stricmp(); memset(); atoi(); atof(); atol() https://beta.wikiversity.org/wiki/Chu%E1%BB%97i_k%C3%BD_t%E1%BB%B1_trong_C https://v1study.com/c-ham-xu-ly-chuoi-string-a56.html https://oktot.net/huong-dan-su-dung-cac-ham-xu-ly-chuoi-trong-c/ Các hàm xử lý chuỗi trong C ▪ Lấy độ dài chuỗi: strlen ▪ Độ 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 ( không đếm ký tự '\0') size_t strlen ( const char * str ); Ví dụ: #include #include #include using namespace std; int main() { char s[20] = "ABC def"; cout << strlen(s) <<endl; // ket qua la 7 char *p = new char[256]; cout << "nhap chuoi:" ; gets(p); int k = strlen(p); cout << k <<endl; delete[] p; return 0; } String ▪ Đổi một ký tự thường thành ký tự hoa - Hàm toupper Cú pháp: char toupper(char c) ▪ Đổi toàn bộ các ký tự của chuỗi thành IN HOA : strupr Cú pháp: char* strupr(char *s) ▪ Đổi toàn bộ các ký tự của chuỗi thành in thường: strlwr Cú pháp: char *strlwr(char *s) #include #include #include using namespace std; int main() { char s[20] = "lap TrinH C"; char c ='a'; cout<< char(toupper(c))<<endl; // A cout<< strupr(s) <<endl; cout<< strlwr(s) <<endl; return 0; } String ▪ Gán/sao chép nội dung chuỗi: o Chép toàn bộ chuỗi source sang chuỗi destination: strcpy Cú pháp: char* strcpy(char *Des, const char *Source) Lưu ý: Mảng kí tự destination phải được cấp phát đủ bộ nhớ để lưu trữ chuỗi kí tự được copy từ mảng kí tự source. o Chép tối đa n ký tự đầu tiên từ source sang dest: strncpy Cú pháp: char * strncpy(char *Des, const char *Source, size_t n) Lưu ý: Visual studio á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 (string.h) String ▪ Tạo chuỗi mới từ chuỗi đã có: strdup char *strdup(const char *src); ▪ Khắc phục được vấn đề mảng kí tự destination phải được cấp phát đủ bộ nhớ để lưu trữ chuỗi kí tự được copy từ mảng kí tự source. ▪ Khi dùng strdup, chuỗi destination đầu tiên được cấp phát bộ nhớ cùng kích thước với chuỗi source, sau đó sẽ tiến hành sao chép nội dung từ source sang destination. #include #include #include using namespace std; int main() { char s1[255], s2[255], s3[255]; cout<<"nhap chuoi:"; gets(s1); cout<<"chuoi s1:"<<s1<<endl; strcpy(s2,s1); // copy s1 to s2 cout<<"chuoi s2:"<<s2<<endl; strncpy(s3,s1, int (strlen(s1) / 2)); // copy 1/2 so luong ky tu cua s1 to s3 s3[int (strlen(s1) / 2)] = '\0'; // whyyyyy???? cout<<"chuoi s3:"<< s3 <<endl; char *s4 = strdup(s1); cout<<"chuoi s4:"<< s4 <<endl; return 0; } String ❖ So sánh chuỗi - so sánh theo thứ tự từ điển : strcmp ▪ Phân biệt IN HOA – in thường: int strcmp(const char *s1, const char *s2); Có 3 TH xảy ra : o strcmp(s1,s2) == 0: hai chuỗi kí tự này hoàn toàn giống nhau char str1[] = "This is a string"; char str2[] = "This is a string"; o strcmp(s1,s2) < 0: chuỗi s1 nhỏ hơn chuỗi s2. Đ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. char str1[] = "abcDEF"; char str2[] = "abcdef"; o strcmp(s1,s2) > 0: chuỗi s1 lớn hơn chuỗi s2. char str1[] = "abcDEF"; char str2[] = “Abcdef"; ▪ Không phân biệt IN HOA – in thường: stricmp int stricmp(const char *s1, const char *s2); #include #include #include using namespace std; int main() { char **s = new char*[5]; for(int i = 0; i < 4; i++) s[i]= new char[255]; char *temp = new char[255]; strcpy(s[0],"Banana"); strcpy(s[1],"Lemon"); strcpy(s[2],"Peach"); strcpy(s[3],"Apple"); for(int i = 0; i < 4; i++) cout<< s[i] << " "; // Sap xep cac word trong s theo thu tu tang dan for(int i = 0; i < 3; i++) for(int j = i+1; j < 4; j++) { if (strcmp(s[i],s[j])>0) { strcpy(temp,s[i]); strcpy(s[i],s[j]); strcpy(s[j],temp); } } cout <<"\nSau khi sap xep" << endl; for(int i = 0; i < 4; i++) cout<< s[i] << " "; return 0; } String ▪ Nối chuỗi: strcat Cú pháp: 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. ▪ Tách chuỗi: strtok Cú pháp : char *strtok(char *s, const char *sep); Trả về địa chỉ của đoạn đầu tiên. Muốn tách đoạn kế tiếp tham số thứ nhất sẽ là NULL #include #include #include using namespace std; int main() { char s[255],t[255]; char c[255]= "Lap Trinh:C++"; cout<<"nhap s:"; gets(s); cout<<"nhap t:"; gets(t); strcat(s,t); cout<<"noi chuoi = " << s <<endl; //strtok // tach 2 chuoi boi dau : cout<< strtok(c,":")<<endl; //cat chuoi tu vi tri dung lai truoc do cout<< strtok(NULL, ":")<<endl; return 0; } String ▪ Tìm một ký tự trên chuỗi: strchr Cú pháp: char *strchr(const char *s, int c); o Tìm kiếm sự xuất hiện đầu tiên của ký tự c (một unsigned char) trong chuỗi được trỏ tới bởi tham số s. o Hàm này trả về một con trỏ tới sự xuất hiện đầu tiên của ký tự c trong chuỗi s, hoặc trả về NULL nếu không tìm thấy ký tự đó. ▪ Tìm một ký tự trên chuỗi: strrchr Cú pháp: char *strrchr(const char *str, int c) o Tìm kiếm sự xuất hiện cuối cùng của ký tự c (một unsigned char) trong chuỗi được trỏ tới bởi tham số str. o Hàm này trả về một con trỏ tới sự xuất hiện cuối cùng của ký tự trong str. Nếu giá trị không được tìm thấy, hàm trả về một con trỏ null. ▪ Tìm một đoạn ký tự trên chuỗi: strstr Cú pháp: const char * strstr ( const char * text, const char * pattern ); 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. #include #include #include using namespace std; int main() { char text[] = "This is a simple string"; char pattern[] = "simple"; char c = 'e'; char *ret = strchr(text, c); cout <<"ket qua:" << ret <<endl; cout << "vi tri tim thay:" << (ret-text) / sizeof(char) << endl; // 15 char *p = strstr(text, pattern); if (p == NULL) { cout << "khong tim thay" << endl; } else { int match_index = (p - text) / sizeof(char); // 10 cout <<"ket qua:" << p <<endl; cout << "vi tri tim thay: " << match_index << endl; } cout in ra gi??? return 0; } String ▪ Đổi chuỗi ký tự sang số : atoi(), atof(), atol(): ▪ Cú pháp: int atoi(const char *s): chuyển chuỗi thành số nguyên long atol(const char *s): chuyển chuỗi thành số nguyên dài float atof(const char *s): chuyển chuỗi thành số thực Nếu chuyển đổi không thành công, kết quả trả về của các hàm là 0. Ví dụ float f; char *str = "12345.67"; f = atof(str); printf("string = %s float = %f\n", str, f); String ❖ The less commonly-used string functions ▪ strrev() ▪ memset(), memcpy(), memcmp(), memmove() ▪ strerror() ▪ https://en.wikibooks.org/wiki/C_Programming/String_manipulation String và FILE ▪ Hàm fputs() – ghi một chuỗi ký tự vào file. Hàm fputs() trong C được sử dụng để ghi một chuỗi ký tự vào file. Cú pháp : int fputs(const char * s, FILE *file) Trong đó: - s là con trỏ trỏ tới địa chỉ của một chuỗi - file: con trỏ tới một file cần ghi #include #include #include using namespace std; int main() { FILE *fp; // open file fp = fopen("file2.txt", "wt"); // write string into file fputs("hello c programming!", fp); // close file fclose(fp); return 0; } String và FILE ▪ Hàm fgets() – đọc một dòng ký tự từ file. Hàm fgets() trong C được sử dụng để đọc một dòng ký tự từ một file đã cho. Cú pháp : char* fgets(char *s, int n, FILE *file) Trong đó: - s là con trỏ kiểu char trỏ tới một vùng nhớ để lưu trữ chuỗi đọc từ file - n là độ dài tối đa cần đọc trên một dòng trong file - file: là con trỏ đến file cần đọc ❑ Lưu ý: ▪ Hàm fgets đọc từ file ra (n - 1) kí tự, việc đọc dữ liệu sẽ bị dừng nếu đọc được kí tự new line '\n' hoặc EOF. ▪ Chuỗi kí tự đọc được sẽ lưu vào vùng nhớ được quản lý bởi con trỏ s, nếu đọc dữ liệu thành công thì trả về địa chỉ của s, ngược lại trả về NULL. #pragma warning(disable:4996) #include #include using namespace std; int main() { // mo file de doc FILE* fi = fopen("F:\\input_4.txt", "rt"); char text[300]; if(fgets(text,200,fi)!= NULL) cout << text << endl; fclose(fi); return 0; } File: file2.txt Ky thuat Lap Trinh String và FILE ▪ Đọc file có nhiều dòng ▪ Mỗi dòng lưu trữ vào một chuỗi ký tự (mỗi chuỗi có ký tự xuống dòng) ▪ Chuỗi cuối file không có ký tự xuống dòng ▪ Xét ví dụ file sau đây chứa 3 chuỗi File: file3.txt C was originally developed at Bell Labs by Dennis Ritchie between 1972 and 1973 to make utilities running on Unix Later it was applied to re-implementing the kernel of the Unix operating system During the 1980s C gradually gained popularity // FILE *fp; // ghi 3 chuoi len file // open file fp = fopen("F:\\output_4.txt", "wt"); // write string into file for(int i=0;i<k;i++) // ghi len file fputs(s[i], fp); fclose(fp); return 0; } #pragma warning(disable:4996) #include #include #include using namespace std; int main() { // mo file de doc FILE* fi = fopen("F:\\input_4.txt", "rt"); char **s; // dung mang dong (con tro cap 2) char *temp = new char[1024]; s = new char*[10]; for(int i=0;i<10;i++) s[i] = new char[1024]; int k =0; while(fgets(temp,1024,fi)!= NULL) { strcpy(s[k],temp); k++; } for(int i=0;i<k;i++) // in 3 chuoi ra man hinh puts(s[i]); fclose(fi); // Có thể dùng mảng tỉnh : char s[10][1024]; Câu hỏi : viết lại chương trình bằng cách chia thành các hàm đọc file và ghi file void readFromFile(FILE *file , char **&s, int &k); void writeToFile(FILE *file, char **s); Ví dụ: ▪ Nhập vào một chuỗi. Đếm số lượng nguyên âm, phụ âm trong chuỗi (Count the Number of Vowels, Consonants) Ví dụ: Input: “ Lap trinh C” Nguyên âm: 2 Phụ âm: 7 #include #include #include using namespace std; int main() { char s[150]; int vowels, consonant, digit, space; vowels = consonant = digit = space = 0; gets(s); for (int i = 0; s[i] != '\0'; ++i) { if (s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u' || s[i] == 'A' || s[i] == 'E' || s[i] == 'I' || s[i] == 'O' || s[i] == 'U') { ++vowels; } else if ((s[i] >= 'a' && s[i] = 'A' && s[i] <= 'Z')) { ++consonant; } else if (s[i] >= '0' && s[i] <= '9') { ++digit; } else if (s[i] == ' ') { ++space; } } cout<<"Vowels: " << vowels <<endl; cout<<"Consonants: " << consonant<<endl; cout<<"Digits: " << digit<<endl; cout<<"White spaces: " << space<<endl; return 0; } Ví dụ: ▪ Đếm số lượng từ trong chuỗi (Count words in a given string) Ví dụ: Input: “ Lap trinh C” . Output: 3 int countWords(char *str) { bool inWord = false; int c = 0; // word count int len = strlen(str); // Scan all characters one by one for(int i=0;i<len;i++) { if (str[i] == ' ' || str[i] == '\n' || str[i] == '\t') inWord = false; // If next character is not a separator and // inWord is False, then set the inWord as True and // increment word count else if (inWord == false) { inWord = true; c++; } } return c; } #include #include #include using namespace std; int countWords(char *str) ; int main() { char *s = new char[255]; gets(s); cout <<"so luong tu:" <<countWords(s) << endl; return 0; } Câu hỏi: - đếm từ bắt đầu bằng nguyên âm - đếm từ bắt đầu bằng phụ âm Ví dụ: ▪ Tách các từ trong chuỗi (Split string by space into words) #include #include #include using namespace std; int main() { char str[] = "ky thuat lap trinh"; char **newstr = new char*[20]; for(int i=0; i<20; i++) newstr[i] = new char[255]; char delim[] = " "; // tach bang khoang trang char *ptr = strtok(str, delim); int n = 0; while(ptr != NULL) { strcpy(newstr[n],ptr); ptr = strtok(NULL, delim); n++; } for(int k=0; k< n;k++) cout<< newstr[k] <<endl; return 0; } Câu hỏi: cách tiếp cận khác không dùng strtok??? Ví dụ: