Lập trình C: Bài 10 – Chuỗi ký tự trong c

Ở bài Nhập xuất trong c chúng ta đã làm quen với cách khai báo, nhập xuất chuỗi ký tự trong C một cách đơn giản. Trong bài này chúng ta sẽ nói nhiều hơn về các ví dụ, bài tập về chuỗi .

1. Tổng quan

Chuỗi được xem như là một mảng 1 chiều gồm các phần tử có kiểu char như ký tự, con số và bất cứ ký tự đặc biệt như +, -, *, /, $, #,…
Theo quy ước, một chuỗi sẽ được kết thúc bởi ký tự null (‘\0’ : kí tựrỗng).
Ví dụ: chuỗi “Infoworld” được lưu trữ như sau:

Cách lưu trữ chuỗi ký tự

Cách lưu trữ chuỗi ký tự

2. Một số cách khai báo, khởi tạo chuỗi

Chúng ta xét ví dụ sau:

// e.g about string - code by nguyenvanquan7826
#include <stdio.h>

int main() 
{
    // khai bao chuoi co toi da 50 ky tu
    char name[50]; 
    printf("Hi, What is your name? \nMy name is: ");
    gets(name);
    printf("Hi %s, welcome to C language\n", name);

    // khoi tao chuoi ngay khi khai bao
    char myLove[] = "Nguyen Thi Lap Lanh";
    puts(myLove);

    return 0;
}

Kết quả:

Hi, What is your name?
My name is: Nguyen Van Quan
Hi Nguyen Van Quan, welcome to C language
Nguyen Thi Lap Lanh

Trong chương trình trên, mình có dùng hàm puts để in chuỗi myLove ra, đây cũng là hàm để xuất chuỗi.

Như trên chúng ta có thể thấy là khai báo chuỗi sau đó nhập chuỗi hoặc vừa khai báo vừa gán giá trị cho chuỗi ngay. Tuy nhiên chúng ta không thể khai báo sau đó mới gán giá trị như sau:

char name[50];
name = "Nguyen Van Quan"; // error

Trong trường muốn khai báo sau đó mới gán giá trị, chúng ta phải dùng hàm copy chuỗi strcpy nằm trong thư viện string.h để lưu giá trị như sau:

// e.g about string - code by nguyenvanquan7826
#include <stdio.h>
#include <string.h>

int main() 
{
    char name[50];
    strcpy(name, "Nguyen Van Quan");
    puts(name);

    return 0;
}

3. Một số ví dụ về chuỗi ký tự trong C

3.1 Ví dụ 1: Đếm số từ trong chuỗi

Hãy nhập vào một chuỗi ký tự và đếm số từ trong chuỗi. VD chuỗi “Nguyen Van Quan” có 3 từ.

Để làm bài này, chúng ta thấy mỗi từ là các ký tự liên tiếp nhau và phân tách các từ bằng các dấu cách. Do vậy bài này sẽ quy về đếm số dấu cách trong chuỗi. Nếu chuỗi có 1 từ thì không có dấu cách, chuỗi có 2 từ thì có 1 dấu cách giữa 2 từ đó. Tổng quát là có n từ thì sẽ có n-1 dấu cách.

Vấn đề tiếp theo là làm sao đếm được các dấu cách? Đơn giản, như mở đầu ta đã biết chuỗi là một mảng các ký tự, do vậy chúng ta có thể duyệt lần lượt các ký tự của chuỗi để kiếm tra ký tự nào là dấu cách. Nhưng muốn duyệt hết các ký trong chuỗi (mảng ký tự) thì phải biết số lượng ký tự có trong chuỗi (số lượng phần tử có trong mảng).

May mắn là trong thư viện string.h chúng ta có một hàm để lấy độ dài của chuỗi là hàm strlen. (str – string, len – length).

// e.g about string - code by nguyenvanquan7826
#include <stdio.h>
#include <string.h> // for strlen function

int main() 
{
    char s[50];
    printf("Enter a string: ");
    gets(s);

    int i, count = 0; // count - bien dem so luong dau cach
    for (i = 0; i < strlen(s); i++ )
    {
        if(s[i] == ' ') 
        {
            count++;
        }
    }

    printf("Number word in string is: %d\n", count + 1 );

    return 0;
}

Code khá đơn giản, các bạn đọc, hiểu và chạy thử nhé.

Tuy nhiên code trên chúng ta có một số lưu ý:

  • Để biểu diễn ký tự thì ta đặt trong cặp nháy đơn, chuỗi thì chúng ta đặt trong cặp nháy kép. Nên ở trên dấu cách đặt trong cặp nháy đơn và chúng ta có thể so sánh 2 ký tự bằng các phép so sánh như với 2 số, còn 2 chuỗi thì không thể so sánh được như vậy, các bạn có thể đọc thêm về cách so sánh chuỗi.
  • Do chuỗi là một mảng các ký tự, nên muốn lấy ký tự thứ i trong chuỗi s thì ta truy cập như với mảng là s[i].
  • Ví dụ này chỉ áp dụng khi chuỗi có độ dài lớn hơn 0 và không có dấu trắng thừa ở đầu, cuối hoặc giữa các từ.
  • Như vòng lặp for ở trên, chúng ta có điều kiện i < strlen(s), tuy nhiên bản chất của hàm strlen là một vòng lặp nữa để đếm số lượng ký tự của chuỗi s. Nên nếu chúng ta viết trực tiếp điều kiện như trên thì trong mỗi lần lặp, chương trình lại phải chạy lại lệnh strlen lặp để đếm số lượng ký tự của s. Điều này là thừa và làm chương trình chạy lâu hơn. Do vậy chúng ta sẽ đặt 1 biến là độ dài của chuỗi ra ngoài như sau:
int len = strlen(s);
for (i = 0; i < len; i++ )
{
    if(s[i] == ' ') 
    {
        count++;
    }
}

3.2 Ví dụ 2: Chuẩn hóa chuỗi

Hãy nhập vào một chuỗi và xóa bỏ toàn bộ các dấu cách thừa ở đầu, cuối và giữa các từ nếu có.

Bài toán này là bài toán quan trọng cho các phần mềm, sau này khi lưu trữ, nhập liệu cần lưu ý để dữ liệu được chuẩn, không thừa thiếu gây sai sót trong quá trình xử lý và tím kiếm.

  • Ký tự đầu tiên của chuỗi s là dấu cách thì s[0] là dấu cách, chúng ta xóa nó là xong.
  • Các ký tự cách giữa các từ nếu thừa tức là s[i] và s[i+1] cùng là dấu cách. Chúng ta xóa 1 trong 2 là ok, vì các từ sẽ phân tách nhau bởi 1 dấu cách nên ta phải dữ lại 1 dấu cách.
  • Các ký tự ở cuối chuỗi là dấu cách thì chúng ta sẽ xóa bằng cách gán ký tự cuối cùng là ký tự rỗng '\0' là xong. Nhớ rằng ký tự cuối cùng của mảng n phần tử là a[n-1], do vậy ký tự của chuỗi là s[ strlen(s) - 1 ].

Vấn đề tiếp theo là làm sao xóa 1 ký tự trong chuỗi? Các bạn nhìn lại ví dụ trên chúng ta có dùng hàm copy chuỗi, và để xóa 1 hoặc một số ký tự trong chuỗi chúng ta sẽ dùng hàm này nhưng sẽ dùng theo copy địa chỉ.

Để xóa từ ký tự i đến ký tự j trong chuỗi s, chúng ta dùng lệnh strcpy(&s[i], &s[j+1]);. Bản chất là chúng ta copy địa chỉ của s[j+1] về địa chỉ của s[i].

// e.g about string - code by nguyenvanquan7826
#include <stdio.h>
#include <string.h>

int main() 
{
    char s[50];
    printf("Enter a string: ");
    gets(s);

    // delete all space at start of string
    while( s[0] == ' ' ) strcpy(&s[0], &s[1]);

    // delete all space at end of string
    while( s[ strlen(s)-1 ] == ' ') s[ strlen(s)-1 ] = '\0';

    // delete all space between two word 

    int i;
    for(i = 0; i < strlen(s); i++)
    {
        if( s[i] == ' ' && s[i+1] == ' ')
        {
            strcpy(&s[i], &s[i+1]);
            i--; // why???
        }
    }

    printf("s=%s.\n", s);

    return 0;
}

Ok. Các bạn chạy thử nhé. Nhớ nhập chuỗi thừa các dấu cách ở đầu, cuối, giữa để kiểm tra.

Một câu hỏi nhỏ coi như bài tập làm thêm cho các bạn là hãy nhìn dòng code i--; // why??? và nghĩ tại sao lại có dòng này? Tại sao i phải giảm đi 1?

4. Một số hàm về chuỗi và ký tự

Các hàm kiểm tra ký tự. (các hàm này trong thư viện ctype.h) Nếu đúng thì hàm cho giá trị khác 0. Nếu sai thì hàm cho giá trị bằng 0.

  • Int isalpha(int c) : kiểm tra ký tự có là chữ cái không.
  • Int isdigit(int c) : kiểm tra xem ký tự có là chữ số không.
  • Int islower(int c): kiểm tra ký tự có là chữ thường không.
  • Int isupper(int c): kiểm tra ký tự có là chữ hoa không.
  • Int ispace(int c): kiểm tra ký tự có là trống không (\n, dấu cách, \t).

Các hàm sử lý xâu ký tự. (các hàm này nằm trong thư viện string.h)

  • Int strlen(char *s) trả về độ dài của xâu s;
  • Char *strupr(char *s) đổi chữ thường trong xâu s sang chữ hoa.
  • Char *strlwr(char *s) đổi chữ hoa sang chữ thường.
  • Char *strcat(char *s1, char *s2) nối xâu s2 vào xâu s1;
  • Int strcmp(char *s1, char *s2) cho giá trị âm nếu xâu s1 nhở hơn xâu s2. Và cho giá tị dương nếu xâu s1 lớn hơn xâu s2. Trả về giá trị bằng 0 nếu xâu s1 bằng xâu s2.
  • Int strcmpi (char *s1, char *s2) so sánh 2 xâu nhưng không phân biệt chữ thường và chữ hoa.
  • Char *strcpy(char *s1, char *s2) copy xâu s2 vào xâu s1.
  • Char *strncpy(char *s1, char *s2, int n) sao chép n ký tự đầu của xâu s2 sang xâu s1
  • Char *strnset(char *s ,int c, int n) dùng để sao chép n lần ký tự c vào xâu s.
  • Char *strstr(char *s1, char *s2) tìm sự xuất hiện của xâu s2 trong xâu s1. Nếu tìm thấy hàm cho địa chỉ của xâu con trong xâu s1. Trái lại cho NULL.
  • Char *strrev(char *s) dùng đảo ngược xâu s.Nếu thành công hàm cho địa chỉ xâu đã đảo.

Bài tập

  1. Viết chương trình tách tên từ một chuỗi cho trước. VD tên Nguyen Thi Lap Lanh => tách được Lanh
  2. Viết chương trình chuẩn hóa chuỗi tên riêng. VD:ha noi => Ha Noi.
  3. Viết hàm chuyển đổi 1 chuỗi sang chữ thường và 1 hàm chuyển đổi sang chữ HOA.
  4. Viết chương trình nhập vào một chuỗi ký tự rồi đếm xem trong chuỗi đó có bao nhiêu chữ “ng”.
  5. Viết chương trình nhập vào một chuỗi ký tự. Kiểm tra xem chuỗi đó có đối xứng không? Chuỗi đối xứng là chuỗi khi viết ngược lại vẫn được như chuỗi ban đầu. VD level
  6. Viết chương trình nhập vào số có 3 chữ số. Cho biết dòng chữ mô tả giá trị con số đó. Ví dụ 123 -> một trăm hai mươi ba.