Bài giảng Kỹ thuật lập trình: Chuỗi ký tự cung cấp cho người học các kiến thức: Khái niệm chuỗi ký tự (string), chuỗi ký tự trong C, nhập/xuất chuỗi ký tự, con trỏ và chuỗi ký tự,...Cuối bài giảng có phần bài tập để người học ôn tập và củng cố kiến thức.
Trang 2▪ Câu hỏi và Bài Tập
▪ Chuỗi ký tự trong C++ (option)
Trang 3Ký 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:
Trang 5Chuỗ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";
- Ký tự đặt trong dấu nháy đơn
- Chuỗi ký tự đặt trong dấu nháy kép
Trang 6Chuỗ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.
cout <<"nhap chuoi:";
return 0;
}
Nhập chuỗi có khoảng trắng, nếu không dùng fflush(stdin)
→ kết quả là gi?
Trang 7Chuỗ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
Trang 8int 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 Bcout<<"Output: ";
puts(B);
return 0; }
String
▪ Có thể dùng hàm puts() để in chuỗi ký tự ra màn hình
Trang 9char other[] = "Tony Blurt";
cout << other << endl;
other[4] = '\0';
cout << other << endl;
return 0;
}
"Blurt" sẽ không được in ra
other
Trang 10String 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à
Trang 11Con 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ự
char *p = new char[50];
cout <<"nhap chuoi:";
gets(p);
cout << "chuoi vua nhap:"<< p << endl;
return 0;
}
Trang 12Con 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.
Trang 13Con 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
Trang 14Con 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 khigặp giá trị '\0'
Trang 16// 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
cout << "chuoi " << i+1 << "la:";cout << s[i] << endl;
}// don dep vung nhofor(int i=0;i<3;i++)
Trang 17http://cunglaptrinh.blogspot.com/2015/01/cac-ham-xu-ly-chuoi-trong-C.html
Trang 18Cá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 );
cout << strlen(s) <<endl; // ket qua la 7 char *p = new char[256];
cout << "nhap chuoi:" ;gets(p);
Trang 19▪ Đổ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<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int main() { char s[20] = "lap TrinH C";
char c ='a';
cout<< char(toupper(c))<<endl; // Acout<< strupr(s) <<endl;
cout<< strlwr(s) <<endl;
return 0;
}
Trang 20▪ Gán/sao chép nội dung chuỗi:
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.
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)
Trang 21▪ 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.
Trang 22strncpy(s3,s1, int (strlen(s1) / 2)); // copy 1/2 so luong ky tu cua s1 to s3
s3[int (strlen(s1) / 2)] = '\0'; // whyyyyy????
Trang 23❖ 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[] = "abc D EF"; char str2[] = "abc d ef";
o strcmp(s1,s2) > 0: chuỗi s1 lớn hơn chuỗi s2
char str1[] = " a bc D EF"; char str2[] = “ A bc d ef";
▪ Không phân biệt IN HOA – in thường: stricmp
int stricmp(const char *s1, const char *s2);
Trang 24s[i]= new char[255];
char *temp = new char[255];
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;
}
Trang 25▪ 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
Trang 26cout<<"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;
}
Trang 27▪ 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.
Trang 28char 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<< strrchr(text, ' ')+1;// ' ‘ khoang trang-> in ra gi???
return 0;
}
Trang 30▪ Đổ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.
Trang 32String 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
return 0;
}
Trang 33- 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
// mo file de docFILE* 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
Trang 34String 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-implementingthe kernel of the Unix operating system
During the 1980s C gradually gainedpopularity
Trang 35char **s; // dung mang dong (con tro cap 2)
char *temp = new char[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);
Trang 36Ví 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”
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] <= 'z') || (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;
Trang 37Ví 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;
Trang 38Ví dụ:
▪ Tách các từ trong chuỗi (Split
string by space into words)
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 trangchar *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???
Trang 39Ví dụ:
▪ Viết chương trình đảo ngược thứ tự các từ có trong chuỗi (Reverse words in a given string)
Ví dụ: Nhập: lap trinh C ( giả sử mỗi từ cách nhau một khoảng trắng)
Xuất ra màn hình là: C trinh lap
strcat(strcat(c,s+i+1)," ");
s[i]='\0';
}strcat(c,s);
puts(c); // in ra man hinh
}
int main() {
char *s = new char[255];
Trang 40Bài Tập
1) Nhập vào một chuỗi s bao gồm các chữ cái in thường hoặc in hoa và khoảng trắng Giả sử một từ là
một dãy liên tiếp các ký tự không chứa khoảng trắng
Giả sử giữa các từ cách nhau duy nhất một khoảng trắng
Giả sử đầu và cuối chuỗi không chứa khoảng trắng
Viết chương trình (hàm) thực hiện các yêu cầu sau :
a) Tách một từ bên phải ( bên trái của chuỗi s)
b) Đưa các ký tự đàu mỗi từ thành chữ in Hoa, còn lại chữ in thường
c) Đếm số lần xuất hiện của chuỗi con y trong s
d) Đếm số lần xuất hiện của các ký tự trong chuỗi s (26 ký tự latin từ a đến z)
e) Tạo chuỗi con được ghép từ từ đầu tiên và từ cuối cùng của chuỗi Giữa 2 từ có một khoảng trắng f) Đảo các ký tự ngược lại trong mỗi từ.
g) Sắp xếp các từ trong chuỗi theo thứ tự tăng dần (theo thứ tự từ điển)
Trang 41Bài tập
2 Nhập vào một chuỗi s bao gồm các chữ cái in thường hoặc in hoa và khoảng trắng Giả sử một từ là một dãy liên tiếp các ký tự không chứa khoảng trắng
Giả sử giữa các từ cách nhau duy nhất một khoảng trắng
Giả sử đầu và cuối chuỗi không chứa khoảng trắng
Viết chương trình (hàm) thực hiện các yêu cầu sau :
a) Đếm xem trong chuỗi s có bao nhiêu nguyên âm, phụ âm.
b) Kiểm tra xem chuỗi s có đối xứng hay không ? Ví dụ: chuỗi: “abc cba” là chuỗi đối xứng
c) Đếm xem chuỗi s có bao nhiêu từ?
d) Trong mỗi từ trong s, đếm xem có bao nhiêu ký tự Tìm từ dài nhất (nhiều ký tự nhất)
e) Đếm số từ có k ký tự trong chuỗi s ( nhập vào k)
f) Tìm chuỗi con gồm m từ (tính từ trái sang phải của chuỗi s) ( nhập vào m)
Trang 42Bài tập
3) Sắp xếp các chuỗi theo thứ tự chữ cái ( dùng string compare) Đọc chuỗi từ file
input.txt, in kết quả đã sắp xếp ra file output.txt
Hint:
• Dùng hàm strcmp để so sánh hai chuỗi
Input.txt Hello
Apple Cat Hi Hat Holiday Candy
Output.txt Apple
Candy Cat Hat Hello Hi Holiday
Trang 44Bài tập
5) File input.txt chứa các chuỗi
- Mỗi chuỗi trên một dòng , phân biệt bởi ký tự xuống dòng
- Đầu và cuối chuỗi không có khoảng trắng
- Giữ các từ có duy nhất một khoảng trắng
* In ra kết quả ra màn hình và ghi vào file output.txt
Đọc chuỗi từ File text
a) Có bao nhiêu chuỗi chứa chuỗi con “and” ?
b) Mỗi chuỗi có bao nhiêu từ có 3 ký tự
c) Tìm một chuỗi có nhiều từ nhất, Có tất cả bao nhiêu từ
d) Sắp xếp các chuỗi tăng theo thứ tự là chiều dài của các chuỗi
e) Đếm xem file Input.txt có tất cả bao nhiêu từ
f) Tìm một từ dài nhất ứng với mỗi chuỗi
g) Tìm chuỗi đảo ngược tương ứng
h) Tìm các chuỗi con chứa các từ tại vị trí 2,3,4,5
(V í dụ chuôi đầu tiên có chuỗi con là “provides constructs that map ”)
Input.txt
C provides constructs that map efficiently to typical machine instructions and has found lasting use in applications previously coded
in assembly language Such applications include operating systems and various application software for computers from supercomputers to embedded systems
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 chua chuoi tieng Anh hoac Tieng Viet khong dau