Hướng dẫn cắt chuỗi trong C | Hàm chuẩn, tự tạo, hỗ trợ đa byte

目次

1. Giới thiệu

Việc thao tác chuỗi trong ngôn ngữ C là một trong những kỹ năng quan trọng khi học lập trình. Đặc biệt, cắt chuỗi (trích xuất chuỗi con) thường được sử dụng khi xử lý dữ liệu hoặc chuyển đổi định dạng.

Trong bài viết này, cách cắt chuỗi trong ngôn ngữ C sẽ được giải thích, bao gồm cách sử dụng các hàm thư viện chuẩn, cách tạo hàm tự viết, hỗ trợ ký tự đa byte (tiếng Nhật), và cách phân tách chuỗi và nhiều hơn nữa. Ngoài ra, chúng tôi cũng sẽ giới thiệu các ví dụ ứng dụng và xử lý lỗi, vì vậy hãy đọc đến cuối.

Những gì bạn sẽ học trong bài viết này

Bằng cách đọc bài viết này, bạn có thể nắm được các kỹ năng sau.

  • C ngôn ngữ lập trình chuỗi ký tự cơ bản khái niệm và ký tự kết thúc vai trò
  • strncpystrchrhàm thư viện chuẩn để trích xuất chuỗi con
  • Hàm tự làm
  • Xem xét ký tự đa byte (tiếng Nhật)
  • strtok phương pháp chia tách chuỗi
  • Cách lấy ký tự trước và sau ký tự cụ thể

Chúng tôi sẽ giải thích kèm các ví dụ mã để người mới bắt đầu cũng dễ hiểu.

Tại sao việc cắt chuỗi trong ngôn ngữ C lại quan trọng?

Ngôn ngữ C xử lý chuỗi như một “mảng (mảng kiểu char)” nên không thể lấy chuỗi con một cách đơn giản như các ngôn ngữ cấp cao khác (Python, JavaScript, v.v.). Do đó, việc chọn phương pháp phù hợp trong các tình huống sau là rất quan trọng.

1. Xử lý dữ liệu đầu vào

Ví dụ, khi phân tích dữ liệu như log hoặc tệp CSV, bạn cần trích xuất các mục cụ thể.

2. Tìm kiếm từ khóa cụ thể

Việc tìm kiếm từ khóa cụ thể trong một chuỗi và lấy thông tin xung quanh nó là điều thiết yếu cho chức năng tìm kiếm và trích xuất dữ liệu.

3. Nâng cao độ an toàn của chương trình

strncpy bằng cách sử dụng đúng các hàm như vậy, bạn có thể ngăn chặn buffer overflow (ghi dữ liệu vượt quá kích thước bộ đệm). Điều này quan trọng để tránh các rủi ro bảo mật.

Cấu trúc của bài viết

Trong bài viết này, chúng tôi sẽ giải thích theo trình tự sau.

  1. Chuỗi trong ngôn ngữ C là gì? Khái niệm cơ bản và tầm quan trọng của ký tự kết thúc
  2. Cách cắt chuỗi con trong ngôn ngữ C【Phần thư viện chuẩn】
  3. C ngôn ngữ cách trích xuất chuỗi con【Tự viết hàm】
  4. Phương pháp cắt chuỗi theo mã ký tự
  5. Cách chia chuỗi trong ngôn ngữ C
  6. Ví dụ ứng dụng: Cách trích xuất trước và sau ký tự cụ thể
  7. Tóm tắt
  8. FAQ

Vậy thì, trước tiên chúng ta hãy xem chi tiết về “Chuỗi trong ngôn ngữ C? Khái niệm cơ bản và tầm quan trọng của ký tự kết thúc”.

2. Chuỗi trong ngôn ngữ C là gì? Khái niệm cơ bản và tầm quan trọng của ký tự kết thúc

2.1 Khái niệm cơ bản về chuỗi trong ngôn ngữ C

Chuỗi là “mảng char”

Trong ngôn ngữ C, chuỗi được xử lý như mảng các ký tự (mảng kiểu char) . Ví dụ, đoạn mã dưới đây là ví dụ cơ bản về định nghĩa và hiển thị chuỗi.

#include <stdio.h>

int main() {
    char str[] = "Hello, World!"; // Define a string literal as an array
    printf("%s ", str); // Output the string
    return 0;
}

Trong đoạn mã này, "Hello, World!" là một chuỗi được lưu trữ như mảng kiểu char, và được xuất ra bằng printf("%s\n", str); .

Cấu trúc bên trong của chuỗi

Chuỗi "Hello" được lưu trữ trong bộ nhớ như sau.

chỉ mục012345
chữHello\0

Trong C, ký tự đặc biệt chỉ kết thúc chuỗi (ký tự null '\0') được tự động thêm vào cuối nên độ dài chuỗi là “số ký tự thực tế + 1” .

2.2 Tầm quan trọng của ký tự kết thúc (ký tự null '

Ký tự null là gì?

'
)

Vấn đề khi không có ký tự null

Ký tự null('\0')là ký tự đặc biệt chỉ kết thúc chuỗi . Để xử lý chuỗi trong C một cách đúng đắn, cần hiểu sự tồn tại của ký tự null này.

#include <stdio.h>

int main() {
    char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // Explicitly specify the null terminator
    printf("%s ", str);                            // Display correctly
    return 0;
}

Trong đoạn mã trên, nếu không có '\0' thì kết thúc của "Hello" sẽ không được nhận diện, có thể gây ra hành vi không mong muốn .

2.3 Cách định nghĩa chuỗi đúng

Như sau, nếu quên ký tự kết thúc có thể gây ra hoạt động bất thường của bộ nhớ.

#include <stdio.h>

int main() {
    char str[5] = {'H', 'e', 'l', 'l', 'o'}; // Does not include the null terminator
    printf("%s ", str);                      // May cause unexpected behavior
    return 0;
}

Nguyên nhân lỗi

  • printf("%s\n", str); ký tự null '\0'
  • Nếu không tồn tại, có thể dữ liệu khác trên bộ nhớ sẽ được xuất ra。

Phương pháp ① Sử dụng literal chuỗi

Phương pháp ② Định nghĩa mảng một cách rõ ràng

Cách định nghĩa chuỗi phổ biến nhất là sử dụng literal chuỗi.

char str[] = "Hello";

Trong cách này, trình biên dịch C sẽ tự động thêm ký tự null '\0' , vì vậy không cần xử lý đặc biệt.

2.4 Cách kiểm tra kích thước chuỗi

Nếu định nghĩa thủ công bao gồm '\0' , thì viết như sau.

char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
  • Kích thước số ký tự và cuối cùng là là quan trọng。
  • Nếu quên đặt vào , sẽ xảy ra hành vi không mong muốn.

Hoạt động của strlen

Để lấy độ dài (số ký tự) của chuỗi, sử dụng hàm strlen .

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello";
    printf("Length of the string: %lu\n", strlen(str)); // Outputs 5 (does not include the null terminator)
    return 0;
}

2.5 Tổng kết

  • strlenký tự null '\0' đếm số ký tự cho đến khi gặp
  • sizeof(str)

3. Cách trích xuất chuỗi con trong ngôn ngữ C [Phiên bản Thư viện chuẩn]

  1. Chuỗi trong ngôn ngữ C được biểu diễn bằng mảng char
  2. Ký tự kết thúc (ký tự null '\0') được dùng để chỉ kết thúc chuỗi, vì vậy phải luôn bao gồm nó
  3. Để lấy độ dài chuỗi strlen sử dụng
  4. Nếu không định nghĩa chuỗi theo cách thích hợp, có thể xảy ra lỗi không mong muốn

3.1 strncpy để lấy chuỗi con

Có thể sử dụng thư viện chuẩn để trích xuất chuỗi con trong C. Trong phần này, chúng tôi sẽ giải thích cách lấy một phần của chuỗi bằng cách sử dụng các hàm thư viện chuẩn như strncpystrchr và các phương pháp sử dụng các hàm thư viện chuẩn để lấy một phần của chuỗi sẽ được giải thích。

strncpy Cú pháp cơ bản

strncpy là hàm sao chép một phần của chuỗi vào bộ đệm khác.

Ví dụ sử dụng cơ bản

char *strncpy(char *dest, const char *src, size_t n);
  • dest
  • src
  • n'\0'

strncpy Lưu ý

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[6];  // Buffer to store the substring

    strncpy(dest, src, 5); // Copy the first 5 characters "Hello"
    dest[5] = '\0';        // Manually add the null terminator

    printf("Substring: %s\n", dest);  // Output "Hello"

    return 0;
}

3.2 strncpy_s Sao chép chuỗi an toàn

  1. ヌル文字 '\0' を手動で追加する必要がある は最大 文字をコピーするが、ため、明示的に を追加する必要があります。
  2. Chú ý tràn bộ đệm kích thước lớn hơn có thể ghi vượt quá bộ đệm。

strncpy_s Cú pháp cơ bản

strncpy_s là phiên bản nâng cao tính an toàn của strncpy, có thể ngăn chặn tràn bộ đệm.

Ví dụ

errno_t strncpy_s(char *dest, rsize_t destsz, const char *src, rsize_t n);
  • dest
  • destszdest
  • src
  • n

strncpy_s Ưu điểm

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[6];

    if (strncpy_s(dest, sizeof(dest), src, 5) == 0) {
        dest[5] = '\0';  // Add null terminator just in case
        printf("Substring: %s\n", dest);
    } else {
        printf("Copy error\n");
    }

    return 0;
}

3.3 strchr Cắt chuỗi đến ký tự xác định

  • Kích thước bộ đệm () để chỉ định, có thể sao chép an toàn.
  • destszn

Tuy nhiên, strncpy_s được thêm vào tiêu chuẩn C11, do đó cần lưu ý rằng một số môi trường có thể không hỗ trợ.

strchr Cú pháp cơ bản

strchr cho phép tìm vị trí của ký tự xác định và lấy chuỗi đến phần đó.

Ví dụ

char *strchr(const char *str, int c);
  • str
  • cchar

Điểm chú ý

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";
    char *pos = strchr(str, ','); // Find the position of ','

    if (pos != NULL) {
        int length = pos - str; // Calculate the number of characters up to ','
        char result[20];

        strncpy(result, str, length);
        result[length] = '\0'; // Add the null terminator

        printf("Substring: %s\n", result);  // Output "Hello"
    }

    return 0;
}

3.4 strstr Tìm kiếm từ khóa và cắt chuỗi

  • strchrcđầu tiên được tìm thấy
  • pos - strstrncpy

strstr Cú pháp cơ bản

strstr hữu ích để tìm kiếm chuỗi con và lấy phần còn lại của chuỗi.

Ví dụ

char *strstr(const char *haystack, const char *needle);
  • haystack
  • needle

Điểm chú ý

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";
    char *pos = strstr(str, "World"); // Search for the position of "World"

    if (pos != NULL) {
        printf("Found substring: %s\n", pos);
    } else {
        printf("Substring not found.\n");
    }

    return 0;
}

3.5 Tổng kết

  • strstrneedle
  • NULLneedlehaystack

4. Phương pháp cắt chuỗi con bằng ngôn ngữ C [phiên bản tự viết hàm]

  1. strncpy khi sử dụng, có thể sao chép chuỗi con an toàn nhưng cần thêm ký tự null thủ công。
  2. strncpy_s cho phép chỉ định destsz ,nâng cao tính an toàn。
  3. strchr Nếu sử dụng, bạn có thể lấy đoạn chuỗi đến một ký tự nhất định。
  4. strstr nếu dùng, bạn có thể lấy vị trí của một từ khóa cụ thể và cắt phần sau đó。

Bằng cách tận dụng thư viện chuẩn, bạn có thể triển khai xử lý chuỗi trong C một cách đơn giản và an toàn.

4.1 Lợi ích của việc tạo hàm tự viết

Nếu tận dụng thư viện chuẩn, việc cắt chuỗi con cơ bản là khả thi, nhưng trong một số trường hợp có thể cần một phương pháp linh hoạt hơn . Vì vậy, trong phần này, chúng tôi sẽ giải thích về việc cắt chuỗi con bằng hàm tự viết .

4.2 Hàm trích xuất chuỗi con cơ bản

Khi sử dụng thư viện chuẩn, có thể sao chép và tìm kiếm chuỗi con, nhưng có những vấn đề như sau.

  • strncpy không tự động thêm ký tự null '\0'
  • strchrstrstr chỉ có thể thực hiện tìm kiếm một phần
  • Thao tác chuỗi linh hoạt hơn là khó khăn

Do đó, việc tạo hàm tự viết có thể tùy chỉnh theo mục đích cụ thể là hiệu quả.

Đặc tả hàm

Đầu tiên, chúng ta sẽ tạo một hàm cơ bản để cỗi từ vị trí chỉ định.

Mã thực thi

  • đối số
  • const char *source
  • int start
  • int length
  • char *dest
  • Nội dung xử lý
  • startlengthdest
  • '\0'

Điểm chú ý

#include <stdio.h>
#include <string.h>

void substring(const char *source, int start, int length, char *dest) {
    int i;
    for (i = 0; i < length && source[start + i] != '\0'; i++) {
        dest[i] = source[start + i];
    }
    dest[i] = '\0'; // Add null terminator
}

int main() {
    char text[] = "Hello, World!";
    char result[10];

    substring(text, 7, 5, result); // Extract "World"
    printf("Substring: %s\n", result);

    return 0;
}

4.3 malloc Lấy chuỗi con động bằng cách sử dụng

  • forlength
  • '\0'
  • dest[i] = '\0'; luôn đặt ký tự null ở cuối

Đặc tả hàm

Trong hàm trên, cần phải dự trữ trước kích thước của dest. Tuy nhiên, nếu có thể dự trữ kích thước cần thiết một cách động, hàm sẽ trở nên đa dụng hơn.

Mã thực thi

  • Cần bộ nhớ được cấp phát bằng
  • startlength
  • Cần thực hiện ở bên gọi

Điểm chú ý

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *substring_dynamic(const char *source, int start, int length) {
    char *dest = (char *)malloc(length + 1); // +1 for the null terminator
    if (dest == NULL) {
        return NULL; // Memory allocation failed
    }

    int i;
    for (i = 0; i < length && source[start + i] != '\0'; i++) {
        dest[i] = source[start + i];
    }
    dest[i] = '\0';

    return dest;
}

int main() {
    char text[] = "Hello, World!";
    char *result = substring_dynamic(text, 7, 5);

    if (result != NULL) {
        printf("Substring: %s\n", result);
        free(result); // Free allocated memory
    } else {
        printf("Memory allocation failed.\n");
    }

    return 0;
}

4.4 Hỗ trợ ký tự đa byte (tiếng Nhật)

  • malloc cấp phát bộ nhớ động
  • Sau khi sử dụng, cần giải phóng bộ nhớ bằng cần giải phóng bộ nhớ.

Triển khai có tính đến ký tự đa byte

Khi làm việc với tiếng Nhật (các ký tự đa byte như UTF-8), một ký tự không nhất thiết chỉ chiếm 1 byte, vì vậy hàm substring đơn giản sẽ không hoạt động đúng.

Mã thực thi (hỗ trợ UTF-8)

  • mbstowcswchar_t
  • wcsncpy
  • wcstombs

Điểm chú ý

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <locale.h>

void substring_utf8(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, ""); // Set the locale

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convert UTF-8 string to wide-character string

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Extract substring in wide characters
    wresult[length] = L'\0';

    wcstombs(dest, wresult, 256); // Convert back to multibyte string
}

int main() {
    char text[] = "こんにちは、世界!"; // UTF-8 string
    char result[20];

    substring_utf8(text, 5, 3, result); // Extract "世界"
    printf("Substring: %s\n", result);

    return 0;
}

4.5 Tổng kết

  • setlocale(LC_ALL, "");
  • mbstowcs
  • wcsncpywcstombs

5. Phương pháp cắt chuỗi theo mã ký tự

  1. substring nếu tự tạo, bạn có thể lấy chuỗi con một cách linh hoạt。
  2. Sử dụng cấp phát bộ nhớ động (malloc) thì có thể lấy được chuỗi con có kích thước thay đổi。
  3. Khi xử lý ký tự đa byte (tiếng Nhật),mbstowcs / wcstombs sử dụng。

Khi thư viện chuẩn như strncpy hoặc strchr khó đáp ứng, việc tạo hàm tự viết có thể làm cho xử lý chuỗi trong ngôn ngữ C mạnh mẽ hơn.

5.1 Trường hợp ASCII (ký tự 1 byte)

Trong ngôn ngữ C, nếu không chú ý đến sự khác nhau của mã ký tự, việc cắt chuỗi có thể không hoạt động đúng . Đặc biệt, khi xử lý các ký tự đa byte như tiếng Nhật (UTF-8, Shift_JIS, EUC-JP, v.v.), vì 1 ký tự không luôn bằng 1 byte, các hàm đơn giản như strncpysubstring không thể xử lý đúng.

Trong phần này, phương pháp cắt chuỗi theo mã ký tự sẽ được giải thích chi tiết.

Lấy chuỗi con cơ bản

Ví dụ thực hiện

Ký tự ASCII là 1 ký tự = 1 byte nên có thể xử lý dễ dàng bằng các hàm strncpy hoặc substring.

5.2 Trường hợp UTF-8 (ký tự đa byte)

#include <stdio.h>
#include <string.h>

void substring_ascii(const char *source, int start, int length, char *dest) {
    strncpy(dest, source + start, length);
    dest[length] = '\0'; // Add null terminator
}

int main() {
    char text[] = "Hello, World!";
    char result[6];

    substring_ascii(text, 7, 5, result); // Extract "World"
    printf("Substring: %s\n", result);

    return 0;
}

Lưu ý

  • ASCII ký tự (chỉ ký tự tiếng Anh và số) nếu strncpy đủ đáp ứng
  • '\0' (ký tự null) phải luôn được thêm vào

Đặc tính của UTF-8

Phương pháp xử lý đúng

Trong UTF-8, số byte của một ký tự có thể thay đổi từ 1-4 byte , vì vậy nếu dùng strncpy đơn giản có thể dẫn đến việc cắt giữa chừng một ký tự.

Lấy chuỗi con hỗ trợ UTF-8

Trong ngôn ngữ C, để xử lý UTF-8 một cách an toàn, nên dùng mbstowcs để chuyển đổi sang chuỗi rộng ( wchar_t ) và sau đó lấy chuỗi con.

5.3 Trường hợp Shift_JIS (ký tự đa byte)

#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>

void substring_utf8(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, ""); // Set the locale

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convert multibyte string to wide-character string

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Get the substring
    wresult[length] = L'\0';

    wcstombs(dest, wresult, 256); // Convert wide-character string back to multibyte
}

int main() {
    char text[] = "こんにちは、世界!"; // UTF-8 string
    char result[20];

    substring_utf8(text, 5, 3, result); // Extract "世界"
    printf("Substring: %s\n", result);

    return 0;
}

Lưu ý

  • setlocale(LC_ALL, "");
  • mbstowcswchar_twcsncpy
  • wcstombs

Đặc tính của Shift_JIS

Lấy chuỗi con hỗ trợ Shift_JIS

Trong Shift_JIS, một ký tự có thể là 1 byte hoặc 2 byte , vì vậy việc dùng strncpy đơn giản sẽ gây ra lỗi hiển thị ký tự.

Triển khai trong Shift_JIS

Trong trường hợp Shift_JIS, cũng nên sử dụng phương pháp chuyển sang chuỗi rộng rồi xử lý .

5.4 Trường hợp EUC-JP (ký tự đa byte)

#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>

void substring_sjis(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, "Japanese"); // Set locale to handle Shift_JIS

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convert multibyte string (Shift_JIS) to wide-character string

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Extract substring
    wresult[length] = L'\0';

    wcstombs(dest, wresult, 256); // Convert wide-character string back to multibyte (Shift_JIS)
}

int main() {
    char text[] = "こんにちは、世界!"; // Shift_JIS string (depending on environment)
    char result[20];

    substring_sjis(text, 5, 3, result); // Extract "世界"
    printf("Substring: %s\n", result);

    return 0;
}

Lưu ý

  • Để xử lý Shift_JIS đúng cách, hãy đặt .
  • mbstowcswcstombs

Đặc tính của EUC-JP

Lấy chuỗi con hỗ trợ EUC-JP

EUC-JP cũng giống như Shift_JIS, do số byte của một ký tự thay đổi, nên cần sử dụng chuyển đổi sang chuỗi rộng .

5.5 Tổng kết

#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>

void substring_eucjp(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, "ja_JP.eucJP"); // Set locale to handle EUC-JP

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convert multibyte string (EUC-JP) to wide-character string

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Extract substring
    wresult[length] = L'\0';

    wcstombs(dest, wresult, 256); // Convert wide-character string back to multibyte (EUC-JP)
}

int main() {
    char text[] = "こんにちは、世界!"; // EUC-JP string (depending on environment)
    char result[20];

    substring_eucjp(text, 5, 3, result); // Extract "世界"
    printf("Substring: %s\n", result);

    return 0;
}

Lưu ý

  • setlocale(LC_ALL, "ja_JP.eucJP");
  • mbstowcswcstombs

6. Cách phân tách chuỗi trong ngôn ngữ C

Mã ký tựsố bytePhương pháp xử lý được khuyến nghị
ASCII1 bytestrncpy
UTF-81-4 bytembstowcswcstombs
Shift_JIS1 hoặc 2 bytembstowcswcstombs
EUC-JP1 hoặc 2 bytembstowcswcstombs
  • Nếu chỉ có ký tự ASCII strncpy được
  • Trong trường hợp UTF-8, Shift_JIS, EUC-JP mbstowcs / wcstombs sử dụng
  • Tùy theo môi trường setlocale(LC_ALL, \"...\"); đặt đúng cách

6.1 strtok để tách chuỗi

Quá trình tách chuỗi được sử dụng trong phân tích dữ liệu CSV, xử lý đối số dòng lệnh, phân tích dữ liệu log và nhiều trường hợp khác. Trong ngôn ngữ C, có thể dùng các hàm thư viện chuẩn như strtok hoặc strtok_r hoặc tự viết hàm riêng.

Trong phần này, chúng tôi sẽ giải thích chi tiết về cách tách chuỗi bằng ký tự phân tách cụ thể.

Cú pháp cơ bản

strtok là hàm tách chuỗi dựa trên ký tự phân tách (delimiter) được chỉ định.

Ví dụ sử dụng: tách chuỗi bằng dấu phẩy ,

char *strtok(char *str, const char *delim);
  • str
  • delim
  • Giá trị trả về
  • Lưu ýstrtok'\0'

Kết quả thực thi

#include <stdio.h>
#include <string.h>


int main() {
    char str[] = "apple,banana,orange,grape"; // String to be split
    char *token = strtok(str, ",");            // Get the first token

    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok(NULL, ",");             // Get the next token
    }

    return 0;
}

strtok lưu ý

token: apple
token: banana
token: orange
token: grape

6.2 strtok_r để tách chuỗi an toàn trong môi trường đa luồng

  1. Thay đổi chuỗi gốc
  • strtokký tự phân cách được thay thế bằng '\0' để,
  1. không an toàn luồng
  • strtoksử dụng biến tĩnh toàn cục bên trong

Cú pháp cơ bản

strtok_r là phiên bản an toàn đa luồng của strtok, lưu trạng thái vào saveptr nên có thể sử dụng an toàn trong môi trường đa luồng.

Ví dụ sử dụng: tách chuỗi bằng dấu cách

char *strtok_r(char *str, const char *delim, char **saveptr);
  • str
  • delim
  • saveptr

Ưu điểm của strtok_r

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello World from C"; // String to be split
    char *token;
    char *saveptr; // Pointer to store internal state

    token = strtok_r(str, " ", &saveptr); // Get the first token
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok_r(NULL, " ", &saveptr); // Get the next token
    }

    return 0;
}

6.3 Tự viết hàm tách chuỗi (không dùng strtok)

  • Thread-safe
  • có thể xử lý nhiều chuỗi song song

Đặc tả hàm tự viết

strtok thay đổi chuỗi gốc, vì vậy có thể tự tạo hàm tách chuỗi mà không làm thay đổi chuỗi gốc.

Mã thực hiện

  • Nhập
  • const char *source
  • const char delim
  • char tokens[][50]
  • Xử lý
  • source
  • delimtokens

Kết quả thực thi

#include <stdio.h>
#include <string.h>

void split_string(const char *source, char delim, char tokens[][50], int *count) {
    int i = 0, j = 0, token_index = 0;

    while (source[i] != '\0') {
        if (source[i] == delim) {
            tokens[token_index][j] = '\0';
            token_index++;
            j = 0;
        } else {
            tokens[token_index][j] = source[i];
            j++;
        }
        i++;
    }
    tokens[token_index][j] = '\0';
    *count = token_index + 1;
}

int main() {
    char text[] = "dog,cat,bird,fish";
    char tokens[10][50]; // Can store up to 10 words
    int count;

    split_string(text, ',', tokens, &count);

    for (int i = 0; i < count; i++) {
        printf("Token: %s\n", tokens[i]);
    }

    return 0;
}

Điểm chú ý

Token: dog
Token: cat
Token: bird
Token: fish

6.4 Ứng dụng tách chuỗi (xử lý dữ liệu CSV)

  • source
  • tokens

Ví dụ phân tích dữ liệu CSV

Dữ liệu CSV (các trường ngăn cách bằng dấu phẩy) có thể được phân tích bằng strtok.

Kết quả thực thi

#include <stdio.h>
#include <string.h>

int main() {
    char csv[] = "Alice,24,Female\nBob,30,Male\nCharlie,28,Male"; // CSV data
    char *line = strtok(csv, "\n"); // Process line by line

    while (line != NULL) {
        char *name = strtok(line, ",");
        char *age = strtok(NULL, ",");
        char *gender = strtok(NULL, ",");

        printf("Name: %s, Age: %s, Gender: %s\n", name, age, gender);

        line = strtok(NULL, "\n");
    }

    return 0;
}

6.5 Tổng kết

Name: Alice, Age: 24, Gender: Female
Name: Bob, Age: 30, Gender: Male
Name: Charlie, Age: 28, Gender: Male

Kết luận

phương phápLợi íchđiểm bất lợi
strtokCó thể chia nhỏ một cách đơn giảnThay đổi chuỗi gốc
strtok_ran toàn luồngCách sử dụng hơi phức tạp
hàm tự viếtKhông thay đổi chuỗi gốcmã trở nên dài
Phân tích CSVđiều tiện cho xử lý dữ liệustrtok Lưu ý hạn chế

7. Ứng dụng: Cách trích xuất phần trước và sau của ký tự cụ thể

  • Nếu chia đơn giản strtok
  • Nếu đa luồng strtok_r
  • Nếu bạn không muốn thay đổi bản gốc, hãy dùng hàm tự làm
  • Có thể áp dụng cho phân tích dữ liệu CSV

Trong phần tiếp theo, chúng tôi sẽ giải thích chi tiết về «Ứng dụng: cách trích xuất ký tự trước và sau một ký tự cụ thể».

7.1 strchr để lấy chuỗi trước ký tự cụ thể

Trong xử lý chuỗi, trích xuất phần trước và sau của ký tự hoặc từ khóa cụ thể thường cần thiết. Ví dụ, có thể có các trường hợp như sau.

  • Lấy phần tên miền duy nhất từ URL
  • Trích xuất tên tệp từ đường dẫn tệp
  • Lấy chuỗi trước và sau các thẻ hoặc ký hiệu cụ thể

Trong ngôn ngữ C, strchrstrstr có thể được sử dụng để thực hiện các xử lý này. Ngoài ra, nếu cần xử lý linh hoạt hơn, việc tạo hàm tự viết cũng là một phương pháp hiệu quả.

Cú pháp cơ bản

strchr khi sử dụng, có thể xác định vị trí của ký tự (được tìm thấy đầu tiên).

Ví dụ: Lấy tên tệp từ đường dẫn tệp

char *strchr(const char *str, int c);
  • str
  • cchar

strchr nếu tìm thấy c, sẽ trả về địa chỉ của nó.

Kết quả thực thi

#include <stdio.h>
#include <string.h>

void get_filename(const char *path, char *filename) {
    char *pos = strrchr(path, '/'); // Search for the last '/'

    if (pos != NULL) {
        strcpy(filename, pos + 1); // Copy from the character after '/'
    } else {
        strcpy(filename, path); // If no '/', copy the whole path
    }
}

int main() {
    char path[] = "/home/user/documents/report.txt";
    char filename[50];

    get_filename(path, filename);
    printf("Filename: %s\n", filename);

    return 0;
}

Điểm chú ý

Filename: report.txt

7.2 strstr để lấy chuỗi sau từ khóa cụ thể

  • strrchr最後に出現した特定の文字(/)の位置を取得
  • pos + 1chỉ tên tệp

Cú pháp cơ bản

strstr khi sử dụng, có thể tìm kiếm chuỗi cụ thể (từ khóa) và lấy chuỗi sau vị trí đó.

Ví dụ: Lấy miền từ URL

char *strstr(const char *haystack, const char *needle);
  • haystack
  • needle

strstr nếu tìm thấy needle, sẽ trả về địa chỉ của vị trí đó.

Kết quả thực thi

#include <stdio.h>
#include <string.h>

void get_domain(const char *url, char *domain) {
    char *pos = strstr(url, "://"); // Search for the position of "://"

    if (pos != NULL) {
        strcpy(domain, pos + 3); // Copy from the character after "://"
    } else {
        strcpy(domain, url); // If "://" is not found, copy the entire string
    }
}

int main() {
    char url[] = "https://www.example.com/page.html";
    char domain[50];

    get_domain(url, domain);
    printf("Domain part: %s\n", domain);

    return 0;
}

Điểm chú ý

Domain part: www.example.com/page.html

7.3 strchr để phân tách phần trước và sau của ký tự cụ thể

  • strstr"https://""http://""//"
  • pos + 3://

Ví dụ: Tách tên người dùng và miền từ địa chỉ email

strchr nếu sử dụng, có thể phân tách và lấy chuỗi trước và sau ký tự cụ thể.

Kết quả thực thi

#include <stdio.h>
#include <string.h>

void split_email(const char *email, char *username, char *domain) {
    char *pos = strchr(email, '@'); // Search for the position of '@'

    if (pos != NULL) {
        strncpy(username, email, pos - email); // Copy the part before '@'
        username[pos - email] = '\0';          // Add null terminator
        strcpy(domain, pos + 1);               // Copy the part after '@'
    }
}

int main() {
    char email[] = "user@example.com";
    char username[50], domain[50];

    split_email(email, username, domain);
    printf("Username: %s\n", username);
    printf("Domain: %s\n", domain);

    return 0;
}

Điểm chú ý

Username: user
Domain: example.com

7.4 Ứng dụng: Trích xuất thuộc tính cụ thể trong thẻ HTML

  • strchr'@'
  • strncpy'@' phần trước, sao chép và thêm ký tự null
  • strcpy'@'

Ví dụ: <a href="URL"> để lấy URL

Ngay cả khi lấy thuộc tính cụ thể từ thẻ HTML, strstr có thể được sử dụng.

Kết quả thực thi

#include <stdio.h>
#include <string.h>

void get_href(const char *html, char *url) {
    char *start = strstr(html, "href=\""); // Search for the position of href="
    if (start != NULL) {
        start += 6; // Move past href="
        char *end = strchr(start, '"'); // Search for the next "
        if (end != NULL) {
            strncpy(url, start, end - start);
            url[end - start] = '\0'; // Add null terminator
        }
    }
}

int main() {
    char html[] = "<a href=\"https://example.com\">Click Here</a>";
    char url[100];

    get_href(html, url);
    printf("Extracted URL: %s\n", url);

    return 0;
}

Điểm chú ý

Extracted URL: https://example.com

7.5 Tổng kết

  • bằng cách sử dụng ” vị trí tìm kiếm và di chuyển 6 ký tự。
  • strchr"

Kết luận

Nội dung xử lýSử dụng hàmlợi ích
Lấy phần trước ký tự cụ thểstrchr / strrchrĐơn giản và nhanh
Lấy phần sau ký tự cụ thểstrstrCó thể tìm kiếm theo từ khóa
Chia trước và sau bằng ký tự cụ thểstrchr + strncpyCó tiện lợi cho việc phân tách tên người dùng・miền, v.v.
Lấy thuộc tính của thẻ HTMLstrstr + strchrCó thể ứng dụng cho web scraping

8. Tóm tắt

  • strchrstrstr を活用すると、特定の文字・キーワードの前後を簡単に取得できる
  • Xử lý đường dẫn tệp, phân tích URL, tách địa chỉ email, v.v., rất hữu ích trong nhiều tình huống
  • Có thể áp dụng cho các xử lý nâng cao như trích xuất dữ liệu web

8.1 Nhìn lại bài viết

Trong bài viết này, Phương pháp cắt chuỗi con trong ngôn ngữ C về điều đó, chúng tôi đã giải thích chi tiết từ cơ bản đến nâng cao. Ở đây, hãy ôn lại các điểm quan trọng của từng phần và sắp xếp phương pháp tối ưu theo mục đích sử dụng.

8.2 Phương pháp tối ưu cho từng mục đích

PhầnNội dungĐiểm quan trọng
C ngôn ngữ chuỗi cơ bảnTrong ngôn ngữ C, chuỗi được coi là mảng , ký tự kết thúc rất quan trọng.Khi xử lý chuỗi,
Phân tách bằng thư viện chuẩnstrncpystrchrstrncpycần thêm ký tự null thủ công
Tách ra bằng hàm tự viếtTạo hàm linh hoạtmalloctrích xuất chuỗi con có độ dài biến thể có thể
Xử lý theo mã ký tựCách xử lý UTF-8, Shift_JIS, EUC-JPmbstowcswcstombs để chuyển đổi sang ký tự rộng là an toàn
Cách phân tách chuỗistrtokstrtok_rstrtokthay đổi chuỗi gốc
Trích xuất ký tự trước và sau ký tự cụ thểstrchrstrstrLấy tên tệp, phân tích URL, phân tích HTML

1. Cắt chuỗi con

2. Phân tách chuỗi

điểm sử dụngphương pháp tối ưu
Tôi muốn lấy một chuỗi có độ dài cố địnhstrncpy or substring()
Muốn thực hiện cắt an toànstrncpy_s
Xử lý ký tự đa byte (UTF-8, Shift_JIS, EUC-JP)mbstowcs / wcstombs

3. Lấy ký tự trước và sau một ký tự cụ thể

điều kiện sử dụngphương pháp tối ưu
Muốn tách chuỗi một cách đơn giảnstrtok
Muốn thực hiện chia tách an toàn luồngstrtok_r
Muốn tách mà không thay đổi chuỗi gốcHàm tự viết(split_string()

8.3 Lưu ý khi xử lý chuỗi trong C

điều kiện sử dụngphương pháp tối ưu
Lấy tên tệp từ đường dẫn tệpstrrchr(path, '/')
Lấy phần tên miền từ URLstrstr(url, "://")
Tách tên người dùng và miền từ địa chỉ emailstrchr(email, '@')
Lấy giá trị thuộc tính từ thẻ HTMLstrstr(tag, "href=\"") + strchr(tag, '"')

1. Quản lý chặt chẽ ký tự kết thúc null ‘__0__’ việc quản lý phải triệt để

Ví dụ sao chép chuỗi an toàn

Trong xử lý chuỗi C, việc quản lý đúng ký tự kết thúc ‘\0’ là quan trọng nhất. Đặc biệt khi sử dụng strncpy hoặc strchr, hãy chú ý phải thêm ký tự null một cách thủ công.

2. Cẩn thận với tràn bộ đệm

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[6];

    strncpy(dest, src, 5);
    dest[5] = '\0'; // Add null terminator for safety

    printf("Substring: %s\n", dest);

    return 0;
}

3. Xử lý ký tự đa byte bằng mbstowcs sử dụng

Trong thao tác chuỗi C, cần cẩn thận để không truy cập ngoài phạm vi mảng. Đặc biệt khi dùng strncpy, việc kiểm soát số byte sao chép là quan trọng.

Ví dụ sao chép chuỗi an toàn

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[6];

    strncpy(dest, src, sizeof(dest) - 1);
    dest[5] = '\0'; // Explicitly add null terminator

    printf("Substring: %s\n", dest);
    return 0;
}

4. Quản lý kích thước bộ đệm

Khi làm việc với ký tự đa byte như UTF-8 hoặc Shift_JIS, strncpy hoặc strlen đơn giản sẽ không hoạt động đúng.

Do đó, khi xử lý ký tự đa byte, nên chuyển sang chuỗi rộng bằng mbstowcs rồi xử lý thích hợp.

#include <stdio.h>
#include <wchar.h>
#include <locale.h>

int main() {
    setlocale(LC_ALL, ""); // Set the locale

    char text[] = "こんにちは、世界!"; // UTF-8
    wchar_t wtext[256];

    mbstowcs(wtext, text, 256); // Convert to wide-character string

    printf("Converted wide-character string: %ls\n", wtext);

    return 0;
}

8.4 Hướng tới việc học sâu hơn

Trong xử lý chuỗi, việc tính toán trước kích thước bộ nhớ cần thiết và ngăn ngừa tràn bộ đệm là quan trọng. Đặc biệt khi dùng malloc để cấp phát bộ nhớ động, hãy chắc chắn biết chính xác kích thước.

Các chủ đề để học sâu hơn

Xử lý chuỗi trong C là kỹ năng quan trọng giúp nâng cao tính an toàn và khả năng đọc của chương trình. Dựa trên nội dung đã giới thiệu, nếu học thêm các chủ đề dưới đây, bạn sẽ có thể thực hiện xử lý chuỗi nâng cao hơn.

8.5 Tóm tắt

  1. Biểu thức chính quy (regex)
  2. Thao tác tệp (xử lý chuỗi bằng fgets, fscanf)
  3. Quản lý bộ nhớ (xử lý chuỗi động bằng malloc, realloc)
  4. Phân tích dữ liệu(Phương pháp phân tích JSON, XML)

8.5 Tóm tắt

  1. Trong ngôn ngữ C, chuỗi là char do được quản lý trong mảng, ký tự kết thúc '\0' việc xử lý là quan trọng
  2. Để cắt chuỗi con, hãy sử dụng strncpy, substring(), malloc
  3. Để tách chuỗi, sử dụng strtok / strtok_r / sử dụng hàm tự viết
  4. Khi muốn lấy ký tự trước và sau một ký tự cụ thể, hãy sử dụng strchr, strstr
  5. Khi xử lý ký tự đa byte (tiếng Nhật), mbstowcs hãy sử dụng
  6. Hãy chú ý đến xử lý chuỗi an toàn, cẩn thận với tràn bộ đệm

Nếu áp dụng nội dung của bài viết này, bạn sẽ có thể thực hiện xử lý chuỗi thực tế trong C. Sau khi hiểu các hàm cơ bản, hãy thử tạo hàm tự viết và các xử lý nâng cao, để viết mã hiệu quả hơn!