Kỹ thuật lập trình Bài 4 – Chuỗi ký tự

Ngô Hữu Dũng

91 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Khái niệm

 Mảng kiểu ký tự

 char word [] = {'H','e','l','l','o','!'};

 Chuỗi ký tự

 char word [] = {'H','e','l','l','o','!','\0'};  char word [] = "Hello!";

 Chuỗi ký tự bao gồm một ký tự đặc biệt nằm cuối chuỗi

 Báo hiệu kết thúc chuỗi  Được hiểu là ký tự NULL, có thể viết là '\0'

 Một chuỗi, ví dụ "Hello!", đã bao hàm ký tự kết thúc, tức char[7]  Kiểu liệt kê, ví dụ {'H','e','l','l','o','!','\0'}, tức char[7]

92 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Khai báo  Cú pháp char [] = ;  Cần xác định kích cỡ của chuỗi

 Khai báo  Khởi tạo một giá trị cho biến  cần đủ lớn để chứa

có thể lớn hơn chiều dài của bao gồm các ký tự và ký tự NULL (\0)

 Nếu không khai báo (để trống)

 Cần khởi tạo chuỗi ban đầu cho biến  Kích cỡ của biến chính là chiều dài của chuỗi (gồm ký tự NULL)

khởi tạo hợp lệ?  nằm giữa dấu hai nháy “”  liệt kê {} các ký tự bao gồm ký tự NULL, ‘\0’, ở cuối cùng 93

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Ví dụ khai báo

1. char hello[] = "Hello!"; // char[7] 2. char hi[] = {'H','i','!','\0'}; // char[4] 3. char name[10]; 4. char classname[20] = " Lop tin hoc "; 5. char city[20] = "TP. HCM"; 6. char empty[] = ""; 7. char empty1[10] = ""; 8. char country[] = {'V','N'};// Array, not string! // Error!? Overflow 9. char gender[2] = "male"; 10. char classroom = "V10.4"; // Error!? Char vs string 11. char university[] = 'IUH'; // Error!? Char vs string 12. char a[];

// Error!? unknown size

94 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Khởi tạo giá trị

 Một khởi tạo giá trị: char hello[] = "Hello!";

 Tương đương với

 char hello[] = {'H','e','l','l','o','!','\0'};  char hello[7] = {'H','e','l','l','o','!','\0'};  char hello[7] = "Hello!";

 Một khởi tạo giá trị: char empty[] = "";

 Tương đương với

 char empty[] = {'\0'};  char empty[1] = {'\0'};  char empty[1] = "";

95 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Khởi tạo giá trị (2)

0

1

2

3

4

5

6

 Một khởi tạo giá trị:

'H'

'i'

'!'

'\0'

 char hi[7] = "Hi!";  char hi[7] = {'H','i','!','\0'};

 Khởi tạo một chuỗi rất dài, dùng dấu \ để xuống dòng

 char longstring[] = "Toi la sinh vien Cong nghe Thong \  tin\nTruong Dai hoc Cong Nghiep TP. HCM.\n\  Than chao cac ban!";

 char longstring[] = "Toi la sinh vien Cong nghe Thong "  "tin\nTruong Dai hoc Cong Nghiep TP. HCM.\n"  "Than chao cac ban!";

96 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Phép gán  Các phép gán trực tiếp cho chuỗi bị hạn chế  Bởi trình biên dịch xử lý với chuỗi như mảng

 Một khởi tạo giá trị: char hello[] = "Hello!";  Các phép gán trực tiếp sau đó bị trình biên dịch báo lỗi  hello = "Good morning!";

// Error!

 “Good morning!” là const char[14] không gán được cho char[7]

 hello = "Hi!";

// Error!

 const char[4] không thể được gán cho char[7]

 hello = hi;

// Error!

 char[4] không thể được gán cho char[7]

97 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Phép gán (2)

 Một khai báo char name[10];

 name = "Quang";

// Error!  const char[6] không gán được cho char[10] // Error!

 name = hello;

 char[7] không gán được cho char[10]

 Làm thế nào để thay đổi giá trị của chuỗi?

 Dùng các hàm xử lý chuỗi như scanf  Thay đổi từng phần tử  Các hàm trong thư viện string.h  Tự viết hàm xử lý chuỗi ký tự

98 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Nhập chuỗi từ bàn phím

 Dùng hàm scanf trong thư viện stdio.h

 scanf("%s",name);

 Nhập "My name is C", name = "My"

 scanf("%[^\n]s",name);

 Nhập "My name is C", name = "My name is C"

 scanf("%[^e]s",name);

 Nhập "My name is C", name = "My nam"

 scanf("%10s",name);

 Nhập "My_name_is_C", name = "My_name_is"

 scanf("%s %s",name1, name2);

 Nhập "My name is C", name1 = "My", name2 = "name"

 scanf tự động thêm ký tự NULL vào sau chuỗi nhận được từ bàn phím

99 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Phần tử của chuỗi

 Phép khởi tạo char hello[] = "Hello!";

 hello[0] = 'H', hello[1] = 'e', hello[2] = 'l',… hello[6] = '\0'

 Phép gán sau đó hello = "Hi!"; sẽ bị báo lỗi!

hello[0]

hello[1]

hello[2]

hello[3]

hello[4]

hello[5]

hello[6]

'H'

'e'

'l'

'l'

'o'

'!'

'\0'

 Có thể thay thế phép gán trên bằng các lệnh:  hello[0] = 'H';  hello[1] = 'i';  hello[2] = '!';  hello[3] = '\0';

 Xuất chuỗi: printf("%s", hello);

 printf("%s", "e");

// "e"= {'e', '\0'}

 Xuất ký tự: printf("%c", hello[1]);

 printf("%c", 'e');

100 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Thư viện string.h

 size_t strlen(const char * str)

 Trả về chiều dài của chuỗi ký tự str  strlen(“hello”) = 5, strlen(hello) = 6  size_t: Unsigned integral type  const char: Bởi hàm không thay đổi giá trị của str

 char * strcpy(char * destination, const char * source)

 Chép nội dung chuỗi source sang chuỗi destination bao gồm ký tự NULL  Chuỗi destination phải đủ chứa chuỗi source (bao gồm ký tự NULL)  Bản thân hàm trả về destination  strcpy(hello, “Hi!”) trả về chuỗi “Hi!” và hello = “Hi!”  char * destination: Hàm thay đổi giá trị của destination  const char * source: Hàm không thay đổi giá trị của source

101 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Thư viện string.h (2)

 char * strncpy(char * destination, const char * source, size_t num)

 Chép num phần tử đầu tiên của source sang destination  Nếu num không đủ lớn để bao gồm ký tự NULL của source,

 hàm không tự gán ký tự NULL vào destination

// hello = “Hi!ol!” // hello = “Hi!”

 strncpy(hello, “Hi!”, 3);  hello[3]=‘\0’;  strncpy(hello, hi, strlen(hi)+1);

// hello = “Hi!”

 char * strcat( char * destination, const char * source )

 Nối chuỗi source vào cuối chuỗi destination  destination phải đủ chứa chuỗi kết quả (bao gồm ký tự NULL)  char name[10]; strcpy(name, hello); strcat(name, hi); // name = “Hello!Hi!”

102 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Thư viện string.h (3)

 char * strncat( char * destination, const char * source, size_t num )

 Nối num ký tự của chuỗi source vào cuối chuỗi destination  Tự động thêm ký tự NULL và chuỗi kết quả (khác với strncpy)  strcpy(name, hello); strncat(name, hi, 2); // name = “Hello!Hi”

 int strcmp( const char * str1, const char * str2 )

 So sánh chuỗi str1 với chuỗi str2, so sánh từng ký tự từ đầu đến khi phát hiện điểm

khác nhau hoặc đến cuối chuỗi (‘\0’)

 Giá trị trả về >0: str1 > str2  Giá trị trả về =0: str1 = str2  Giá trị trả về <0: str1 < str2

 int strcoll( const char * str1, const char * str2 )

 Tương tự strcmp

103 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Thư viện string.h (4)

 int strncmp ( const char * str1, const char * str2, size_t num )

 So sánh num ký tự trong chuỗi str2 với chuỗi str1

 const char * strchr( const char * str, int character)

 Trả về con trỏ (địa chỉ) của vị trí đầu tiên tìm được ký tự character trong chuỗi str  printf("%d",strchr(hello,'o')-hello+1);

// In ra số 5, tìm thấy ký tự ‘o’

 size_t strcspn( const char * str1, const char * str2)

 Trả về vị trí của phần tử trên str1 giống với ký tự bất kỳ trong str2  printf("%d",strcspn(hello,"I got it!")+1);

// In ra số 5, tìm thấy ký tự ‘o’

 const char * strpbrk( const char * str1, const char * str2)

 Giống strcspn nhưng trả về con trỏ (địa chỉ) của ký tự được tìm thấy hoặc NULL nếu

không tìm thấy

// In ra số 5, tìm thấy ký tự ‘o’

 printf("%d",strpbrk(hello,"I got it!")-hello+1); 104

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Thư viện string.h (5)

 const char * strrchr( const char * str, int character )  Giống strchr nhưng tìm phần từ cuối cùng của chuỗi str  printf("%d",strrchr(hello, 'l')-hello+1);

// In ra số 4, tìm thấy ký tự ‘l’ sau

 size_t strspn( const char * str1, const char * str2): Trả về số ký tự đầu tiên

// = 1, tìm được chữ ‘H’

trong str1 trùng với bất kỳ ký tự nào trong str2  strspn(hello,hi);  strspn(hello, “!olleH”);  strspn(hello, “Heyo!”);

// = 6, tìm được các chữ H, e, l, l, o, ! // = 2, tìm được các chữ H, e, chữ o không được tính vì

không tìm được chữ ‘l’ theo thứ tự

 const char * strstr( const char * str1, const char * str2 ): Trả về con trỏ (địa

chỉ) của vị trí tìm thấy str2 trong str1  printf("%d",strstr(hello,"lo")-hello+1);

// = 4

105 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Thư viện string.h (6)  char * strtok( char * str, const char * delimiters): Chia chuỗi thành các chuỗi

// “Lop”

con  Char classname[20] = “ Lop tin:11”;  printf("%s\n",strtok(classname," :"));  printf("%s\n",strtok(NULL," :"));  printf("%s\n",strtok(NULL," :"));

// “Tin” // “11”

 void * memset( void * ptr, int value, size_t num): Điền giá trị value vào num

bytes ô nhớ đầu tiên của biến ptr  memset(hello,'2',2);

// hello = “22llo!”

 void * memcpy( void * destination, const void * source, size_t num ): Chép

num ô nhớ đầu tiên của biến source vào biến destination  memcpy(hello,hi,3);

// hello = “Hi!lo!”

106 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Thư viện string.h (7)

 void * memmove( void * destination, const void * source, size_t num): Giống

memcpy nhưng cho phép chồng lấn  memmove(hello,hi,3); // hello = “Hi!lo!”  memcpy(hello,hello+3,3); // hello = “lo!lo!”

trường hợp chồng lấn

 int memcmp( const void * ptr1, const void * ptr2, size_t num): So sánh ô nhớ  const void * memchr( const void * ptr, int value, size_t num ): Tìm kiếm  Ghi chú: Các lệnh về memory (mem) không phân biệt kiểu dữ liệu, chỉ thao tác

với dữ liệu binary trong các ô nhớ

 void * memset( void * ptr, int value, size_t num): Điền giá trị value vào num

bytes ô nhớ đầu tiên của biến ptr  memset(hello,'2',2);

// hello = “22llo!”

107 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Thư viện string.h (8)  void * memcpy( void * destination, const void * source, size_t num )

 Chép num ô nhớ đầu tiên của biến source vào biến destination  memcpy(hello,hi,3);

// hello = “Hi!lo!”  void * memmove( void * destination, const void * source, size_t num)

 Giống memcpy nhưng cho phép chồng lấn // hello = “Hi!lo!”  memmove(hello,hi,3);  memcpy(hello,hello+3,3); // hello = “lo!lo!”

trường hợp chồng lấn

 int memcmp( const void * ptr1, const void * ptr2, size_t num)

 So sánh num ô nhớ

 const void * memchr( const void * ptr, int value, size_t num )

 Tìm kiếm giá trị value

 Ghi chú: Các lệnh về memory (mem) không phân biệt kiểu dữ liệu, chỉ thao tác

với dữ liệu binary trong các ô nhớ 108

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Chuyển đổi giữa chuỗi và số

 Thư viện chứa các hàm chuyển đổi giữa chuỗi và số  int atoi (const char * str)

 Chuyển chuỗi str sang số nguyên và trả về giá trị số nguyên  atoi("34.5 53") = 34

 double atof (const char* str)

 Chuyển đổi một chuỗi sang kiểu số thực và trả về số thực  atof("34.5 53") = 34.5

 long int atol ( const char * str )

 Chuyển đổi chuỗi sang kiểu long int (4 bytes)

 long long int atoll ( const char * str )

 Chuyển đổi chuỗi sang kiểu long long int (8 bytes) 109

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Chiều dài của chuỗi

 Chiều dài của chuỗi được xác định dựa vào ký tự NULL, kết thúc chuỗi

 Viết hàm tính chiều dài của chuỗi

 Hàm có kiểu nguyên, trả về chiều dài của chuỗi (output)  Đối số của hàm là một chuỗi (input)  Mã giả:

 Khai báo một biến đếm và khởi tạo giá trị 0  Duyệt và đếm các phần tử của chuỗi  Dừng đếm khi gặp ký tự ‘\0’  Trả về chiều dài của chuỗi dựa vào giá trị của biến đếm

 Tương tự hàm strlen của thư viện string.h

110 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Ví dụ tham khảo hàm tính chiều dài của chuỗi

1. int stringLength(const char str[]) 2. { 3.

4.

int count = 0; while (str[count] != '\0')

5.

count++; return count;

6. 7. } 8. 9. int main() 10. { 11.

char hello[] = "Hello!"; printf("%d",stringLength(hello));

12. }

111 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Chép chuỗi

 Viết hàm chép nội dung của một chuỗi nguồn vào một chuỗi đích

 Chuỗi đích sẽ mang giá trị của chuỗi nguồn (output)  Đối số của hàm là hai chuỗi, nguồn và đích (input)  Mã giả

 Khai báo một biến chạy và khởi gán giá trị ban đầu  Duyệt từng phần tử của chuỗi nguồn  Gán giá trị của từng phần tử ở chuỗi nguồn sang chuỗi đích  Dừng sao chép khi gặp ký tự ‘\0’  Gán ký tự ‘\0’ vào cuối chuỗi đích

 Tương tự lệnh strcpy của thư viện string.h  Hàm có kiểu trả về là void

 Bạn có thể trả về chuỗi đích cho hàm nếu biết dùng con trỏ, pointer (sẽ học sau)

112 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Ví dụ tham khảo hàm chép chuỗi

1. void stringCopy(char str1[], const char str2[]) 2. { 3.

4.

int i=-1; do{

5.

6.

i++; str1[i]=str2[i];

}while (str2[i]!='\0');

7. 8. } 9. int main() 10. { 11.

12.

char hello[10]; stringCopy(hello,"Hello!"); printf("%s",hello);

13. 14. }

113 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Nối chuỗi

 Viết hàm nối một chuỗi nguồn vào cuối một chuỗi đích  Chuỗi đích sẽ mang giá trị là kết quả của việc nối hai chuỗi  Đối số của hàm gồm hai chuỗi  Mã giả

 Khai báo hai biến chạy và khởi gán giá trị ban đầu

 Biến chạy cho chuỗi nguồn, bắt đầu từ đầu chuỗi nguồn  Biến chạy cho chuỗi đích, bắt đầu từ cuối chuỗi đích

 Duyệt từng phần tử của chuỗi nguồn  Gán giá trị của các phần tử của chuỗi nguồn vào chuỗi đích  Dừng sao chép khi gặp ký tự NULL của chuỗi nguồn  Gán ký tự NULL vào chuỗi đích

 Hàm tương tự như hàm strcpy trong thư viện string.h

114 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Ví dụ tham khảo hàm nối chuỗi

1. void stringAppend(char dest[], const char src[]) 2. { 3.

4.

int i = stringLength(dest)-1, j = -1; do{

5.

6.

i++;j++; dest[i]=src[j];

}while(src[j]!='\0');

7. 8. } 9. int main() 10. { 11.

12.

char thanks[20] = "Thank"; stringAppend(thanks, " you!"); printf("%s",thanks);

13. 14. }

115 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

So sánh chuỗi

 Viết hàm so sánh hai chuỗi ký tự str1 và str2

 Hàm trả về giá trị  < 0 nếu str1 < str2  = 0 nếu str1 = str2  > 0 nếu str1 > str2

 Hàm có 2 đối số là hai chuỗi cần so sánh  Mã giả

 Khai báo và khởi tạo cho một biến chạy  Duyệt các phần tử cho đến khi

 Gặp phần tử khác nhau  Hoặc kết thúc một trong hai chuỗi

 So sánh phần tử cuối cùng và trả về giá trị so sánh tương ứng

116 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Ví dụ tham khảo về hàm so sánh chuỗi

1. int stringCompare(const char str1[], const char str2[]) 2. { 3.

4.

int i = 0; while(str1[i]==str2[i]&&str1[i]!='\0'&&str2[i]!='\0')

5.

i++;

6.

if(str1[i]>str2[i])

7.

return 1;

8.

else if (str1[i]

9.

return -1;

10.

else

return 0;

11. 12. };

117 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Tìm kiếm

 Viết hàm tìm kiếm vị trí của một chuỗi nguồn trong chuỗi đích

 Hàm trả về số nguyên là vị trí đầu tiên tìm thấy  Hàm trả về số âm nếu không tìm thấy  Đối số của hàm là hai chuỗi

 Mã giả?  ?  ?  ?  ?

118 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Ví dụ tham khảo về tìm kiếm chuỗi

 Hoạt động??

1.

2.

int stringSearch(const char str1[], const char str2[]) {

3.

4.

5.

int i=0, j=0; while(str1[i]!='\0' && str2[j]!='\0') {

6.

7.

if(str1[i+j]!=str2[j]) {

8.

i++; j=0;

9.

10.

} else

11.

12.

j++; if (str2[j]=='\0')

13.

return i;

14.

} return -1;

15. 16. }

119 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Hết bài 4

 Khái niệm chuỗi

 Bài tập về nhà

 Nhập vào họ và tên, đếm số ký tự alphabet  Ví dụ: Nhập “Tran Van An ”, xuất ra có 9 ký tự

 Khai báo, khởi tạo

 Nhập vào một chuỗi, đếm số chữ

 Ví dụ: Nhập “Day la mot chuoi ky tu”, xuất ra có 6 chữ

 Phần tử của chuỗi

 Nhập vào họ và tên, xuất ra họ, chữ đệm và tên

 Ví dụ: Nhập “Tran Van An”, xuất ra họ Tran, chữ đệm

Van, tên An

 Thư viện string.h

 Nhập vào một số, xuất ra chữ viết của số đó

 Ví dụ: Nhập 456, xuất ra: Bon tram nam muoi sau

 Thư viện stdlib.h

 Một vài bài toán về chuỗi

120 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng