C語言字串切割完整指南|標準函式、自製函式、支援多位元組

目次

1. 前言

在 C 語言中,字串操作是學習程式設計時重要的技能之一。特別是,字串切割(子字串的抽取)在處理資料與格式轉換時常被使用。 本文將詳細說明C 語言中字串切割的方法,包括使用標準函式庫函式的方法、自製函式的製作方法、對多位元組字元(日文)之支援、字串分割方法等。此外,還會介紹應用範例與錯誤處理,請務必閱讀至最後。

本文可學到的內容

閱讀本文後,您將掌握以下技能。
  • C 語言字串的基本概念與終止字元 ' ' 的作用
  • strncpystrchr使用標準函式庫函式進行子字串切割
  • 使用自製函式的字串操作實作方法
  • 考慮多位元組字元(日文)之UTF-8 與 Shift_JIS 字串處理
  • 使用 strtok字串分割方法
  • 取得特定字元前後的方法及其應用範例
即使是初學者也能輕鬆理解,我們將結合程式碼範例進行說明。

為何在 C 語言中字串切割很重要?

C 語言將字串視為「陣列(char 型陣列)」,因此無法像其他高階語言(如 Python、JavaScript 等)那樣輕鬆取得子字串。因此,在以下情境中選擇適當的方法相當重要。

1. 輸入資料的處理

例如,在解析日誌資料或 CSV 檔等資料時,需要抽取特定項目。

2. 搜尋特定關鍵字

在字串中尋找特定關鍵字,並取得其前後資訊,對於搜尋功能與資料抽取是必不可少的。

3. 提升程式安全性

透過適當使用 strncpy 等函式,可防止緩衝區溢位(寫入超過緩衝區大小的資料)。這對於避免安全風險相當重要。

本文結構

本文將依照以下流程說明。
  1. C 語言的字串是什麼?基本概念與終止字元的重要性
  2. C 語言中子字串切割方法【標準函式庫篇】
  3. C 語言中子字串切割方法【自製函式篇】
  4. 依照字元編碼的字串切割方法
  5. C 語言中字串分割的方法
  6. 應用範例:取得特定字元前後的方法
  7. 總結
  8. FAQ
那麼,首先讓我們詳細探討「C 語言的字串是什麼?基本概念與終止字元的重要性」。

2. C 語言的字串是什麼?基本概念與終止字元的重要性

2.1 C 語言字串的基本概念

字串是「char 陣列」

在 C 語言中,字串被視為字元陣列(char 型的陣列)。例如,以下程式碼是字串定義與顯示的基本範例。
#include <stdio.h>

int main() {
    char str[] = "Hello, World!"; // 將字串常值定義為陣列
    printf("%s
", str); // 輸出字串
    return 0;
}
在此程式碼中,字串 "Hello, World!"char 型的陣列儲存,並透過 printf("%s", str); 輸出。

字串的內部結構

字串 "Hello" 在記憶體中會如下儲存。
索引012345
字元Hello
在 C 語言中,表示字串結尾的特殊字元(空字元 ' ')會自動在最後加入,因此字串長度為 「實際字元數 + 1」

2.2 終止字元(空字元 ' ')的重要性

什麼是空字元?

空字元(' ')是表示字串結尾的特殊字元。要正確處理 C 語言的字串,需要了解此空字元的存在。
#include <stdio.h>

int main() {
    char str[6] = {'H', 'e', 'l', 'l', 'o', ' '}; // 明確指定終止字元
    printf("%s
", str); // 正確顯示
    return 0;
}
在上述程式碼中,若沒有 ' ',則 "Hello" 的結尾不會被識別,可能會發生未預期的行為

缺少空字元時的問題

如以下所示,若忘記終止字元,可能會導致記憶體異常運作
#include <stdio.h>

int main() {
    char str[5] = {'H', 'e', 'l', 'l', 'o'}; // 未包含空字元
    printf("%s
", str); // 發生未預期的行為



    return 0;
}
錯誤的原因
  • printf("%s", str);持續輸出字元,直到找到空字元 ' '
  • 如果沒有 ' ',可能會輸出記憶體中其他資料。

2.3 正確的字串定義方法

方法① 使用字串常值

最常見的字串定義方式是使用字串常值。
char str[] = "Hello";
使用此方法時,C 編譯器會自動加入空字元 ' ',因此不需要額外處理。

方法② 明確定義陣列

若手動包含 ' ' 進行定義,請如下寫法。
char str[6] = {'H', 'e', 'l', 'l', 'o', ' '};
  • 需要指定字元數 +1 的大小,並在最後加入 ' '
  • 如果忘記在 str[5] 放入 ' ',會發生未預期的行為。

2.4 確認字串大小的方法

要取得字串長度(字元數),請使用 strlen 函式。
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello";
    printf("字串長度: %lu
", strlen(str)); // 輸出 5(不含空字元)
    return 0;
}

strlen 的運作

  • strlen計算直到出現空字元 ' ' 為止的字元數
  • sizeof(str) 不同,取得的是「實際字串的長度」而非陣列大小。

2.5 小結

  1. C 語言的字串以 char 陣列表示
  2. 終止字元(空字元 ' ')表示字串結尾,必須包含
  3. 取得字串長度請使用 strlen
  4. 若未以適當方式定義字串,可能會發生未預期的錯誤

3. C 語言中切割子字串的方法【標準函式庫篇】

在 C 語言中切割子字串,可以利用標準函式庫的方法。本節將說明使用 strncpystrchr標準函式庫函式來部分取得字串的方法

3.1 使用 strncpy 取得子字串

strncpy 是一個將字串的一部份複製到另一個緩衝區的函式

strncpy 的基本語法

char *strncpy(char *dest, const char *src, size_t n);
  • dest: 複製目的地的緩衝區
  • src: 複製來源的字串
  • n: 要複製的最大字元數(不含 ' '

基本使用範例

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

int main() {
    char src[] = "Hello, World!";
    char dest[6];  // 用於存放子字串的緩衝區

    strncpy(dest, src, 5); // 複製前5個字元 "Hello"
    dest[5] = ' ';  // 手動加入空字元

    printf("子字串: %s
", dest);  // 輸出 "Hello"

    return 0;
}

strncpy 的注意事項

  1. 需要手動加入空字元 ' ' strncpy 會複製最多 n 個字元,但不會自動加入 ' ',因此必須明確地加入 dest[n] = ' ';
  2. 注意緩衝區溢位 如果 n 大於 dest 的大小,可能會寫入緩衝區之外。

3.2 使用 strncpy_s 的安全字串複製

strncpy_s 是加強 strncpy 安全性的版本,可防止緩衝區溢位。

strncpy_s 的基本語法

errno_t strncpy_s(char *dest, rsize_t destsz, const char *src, rsize_t n);
  • dest: 複製目的地的緩衝區
  • destsz: dest 的大小
  • src: 複製來源的字串
  • n: 要複製的最大字元數

使用範例

#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] = ' ';  // 為了保險起見,加入空字元
        printf("子字串: %s
", dest);
    } else {
        printf("複製錯誤
");
    }

    return 0;
}

strncpy_s 的優點

  • 可指定緩衝區大小 (destsz),因此能安全複製。
  • 如果 n 大於 destsz,會回傳錯誤。
但需注意,strncpy_s 是在 C11 標準中加入的,部分環境可能無法使用。

3.3 使用 strchr 切割至特定字元

使用 strchr 可以找到特定字元的位置,並取得至該字元之前的字串。

strchr 的基本語法

char *strchr(const char *str, int c);
  • str: 要搜尋的字串
  • c: 想要尋找的字元(char 型)

使用範例

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

int main() {
    char str[] = "Hello, World!";
    char *pos = strchr(str, ','); // 尋找 ',' 的位置

    if (pos != NULL) {
        int length = pos - str; // 計算至 ',' 的字元數
        char result[20];

        strncpy(result, str, length);
        result[length] = ' '; // 加入空字元

        printf("子字串: %s
", result);  // 輸出 "Hello"
    }

    return 0;
}

要點

  • strchr回傳第一個找到的 c 的位址,因此可用來決定子字串的範圍。
  • 利用 pos - str 計算子字串長度,然後用 strncpy 複製,即可取得至特定字元的子字串。

3.4 使用 strstr 進行關鍵字搜尋與切割

strstr 用於搜尋子字串,並取得其之後的字串,非常方便。

strstr 的基本語法

char *strstr(const char *haystack, const char *);
  • haystack: 要搜尋的字串
  • needle: 要搜尋的子字串

使用範例

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

int main() {
    char str[] = "Hello, World!";
    char *pos = strstr(str, "World"); // 搜尋 "World" 的位置

    if (pos != NULL) {
        printf("找到的子字串: %s
", pos);
    } else {
        printf("找不到子字串。
");
    }

    return 0;
}

要點

  • strstr 會回傳第一個找到的 needle 的指標。
  • 如果回傳 NULL,表示 needle 不存在於 haystack 中。

3.5 總結

  1. 使用 strncpy 可以安全地複製子字串,但需要手動加入空字元。
  2. strncpy_s 可以指定 destsz,提升安全性。
  3. 使用 strchr 可以取得至特定字元的子字串。
  4. 使用 strstr 可以取得特定關鍵字的位置,並切割其之後的字串。
活用標準函式庫,可在 C 語言中簡潔且安全地實作字串處理。

4. C語言切割子字串的方法【自製函式篇】

只要善用標準函式庫,就能進行基本的子字串切割,但視情況可能需要更彈性的方式。因此,本節將說明使用自製函式的子字串切割

4.1 製作自製函式的好處

使用標準函式庫可以進行子字串的複製與搜尋,但會有以下問題。
  • strncpy 不會自動加入空字元 '\0'
  • strchrstrstr 只能進行部分搜尋
  • 更彈性的字串操作較為困難
因此,依據特定用途客製化的自製函式是有效的做法。

4.2 基本的子字串抽取函式

首先,建立一個從指定位置切割字串的基本函式。

函式規格

  • 參數
  • const char *source(原始字串)
  • int start(起始位置)
  • int length(要切割的字元數)
  • char *dest(存放切割後字串的緩衝區)
  • 處理內容
  • 將從 start 起的 length 個字元複製到 dest
  • 自動加入 '\0'

實作程式碼

#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] != ' '; i++) {
        dest[i] = source[start + i];
    }
    dest[i] = ' '; // 加入空字元
}

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

    substring(text, 7, 5, result); // 切割 "World"
    printf("子字串: %s
", result);

    return 0;
}

要點

  • for 迴圈會複製指定的 length 個字元。
  • 遇到 ' ' 時結束迴圈。
  • 加入 dest[i] = ' ';確保一定在末尾放置空字元

4.3 malloc 用於動態子字串取得

在上述函式中,需要事先確保 dest 的大小。但若能動態分配所需大小,函式會更通用。

函式規格

  • 使用 malloc 取得所需記憶體
  • 將從 start 起的 length 個字元子字串複製到新緩衝區
  • 呼叫端必須使用 free 釋放

實作程式碼

#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
    if (dest == NULL) {
        return NULL; // 記憶體分配失敗時
    }

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

    return dest;
}

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

    if (result != NULL) {
        printf("子字串: %s
", result);
        free(result); // 釋放記憶體
    } else {
        printf("記憶體分配失敗。
");
    }

    return 0;
}

要點

  • 使用 malloc 動態分配記憶體,因此不必在意緩衝區大小。
  • 使用完畢後需以 free(result); 釋放記憶體。

4.4 多位元組字元(日本語)支援

在處理日文(UTF-8 等多位元組字元)時,因為一個字元不一定是 1 位元組,單純的 substring 函式無法正確運作。

考慮多位元組字元的實作

  • 使用 mbstowcs,將多位元組字串轉換為寬字元字串(wchar_t
  • 使用 wcsncpy 取得子字串
  • 使用 wcstombs 再轉回多位元組字串

實作程式碼(UTF-8 支援)

#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, ""); // 設定 locale

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // 將 UTF-8 字串轉換為寬字元字串

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // 使用寬字元字串切割
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256); // 轉回多位元組字串
}

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

    substring_utf8(text, 5, 3, result); // 取得 "世界"
    printf("子字串: %s
", result);

    return 0;
}

要點

  • 使用 setlocale(LC_ALL, ""); 設定 locale,以支援多位元組。
  • 使用 mbstowcs 將多位元組字串轉換為寬字元字串。
  • 使用 wcsncpy 取得子字串後,wcstombs 再轉回多位元組字串。

4.5 小結

  1. 自製 substring 函式即可彈性取得子字串。
  2. 使用動態記憶體分配 (malloc),即可取得可變大小的子字串。
  3. 處理多位元組字元(如日文)時,活用 mbstowcs / wcstombs
若標準函式庫的 strncpystrchr 難以應對時,透過自製函式即可讓 C 語言的字串處理更為強大。

5. 依照字元編碼的字串切割方法

在 C 語言中,如果不注意字元編碼的差異,字串切割處理可能無法正確運作。特別是處理像日文這樣的多位元組字元(UTF-8、Shift_JIS、EUC-JP 等)時,因為一個字元不等於一個位元組,單純使用 strncpysubstring 函式無法適當處理。 本節將詳細說明依照字元編碼的字串切割方法

5.1 ASCII(單位元組字元)的情況

基本的子字串取得

ASCII 文字因為 1字元 = 1位元組,所以可以使用 strncpysubstring 函式輕鬆處理。
實作範例
#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] = ' '; // 加入 NUL 字元
}

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

    substring_ascii(text, 7, 5, result); // "World" 取得
    printf("子字串: %s\n", result);

    return 0;
}
重點
  • 對於 ASCII 文字(僅英數字)的情況,使用 strncpy 已足夠應對
  • 必須一定要加入 ' '(NUL 字元)

5.2 UTF-8(多位元組字元)的情況

UTF-8 的特性

在 UTF-8 中,單一字元的位元組數為 1~4 位元組 可變,若直接使用 strncpy 可能會在字元中間被截斷。

正確的處理方法

在 C 語言中,若要安全處理 UTF-8,建議使用 mbstowcs 轉換成寬字元字串(wchar_t,再取得子字串。
支援 UTF-8 的子字串取得
#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, ""); // 設定 locale

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // 將多位元組字串轉換為寬字元字串

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // 取得子字串
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256); // 再次將寬字元字串轉換回多位元組字串
}

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

    substring_utf8(text, 5, 3, result); // "世界" 取得
    printf("子字串: %s\n", result);

    return 0;
}
重點
  • 如果不使用 setlocale(LC_ALL, ""); 設定 locale,則多位元組字元無法正確處理。
  • 使用 mbstowcs 將多位元組字串轉換為 wchar_t,再以 wcsncpy 安全處理。
  • 使用 wcstombs 再次將寬字元字串轉回多位元組字串。

5.3 Shift_JIS(多位元組字元)的情況

Shift_JIS 的特性

在 Shift_JIS 中,1字元可能是 1 位元組或 2 位元組,因此直接使用 strncpy 會導致文字亂碼。

支援 Shift_JIS 的子字串取得

在 Shift_JIS 的情況下,也建議使用 轉換為寬字元字串後處理的方法
Shift_JIS 的實作
#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"); // 設定處理 Shift_JIS 的 locale

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256);

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length);
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256);
}

int main() {
    char text[] = "こんにちは、世界!"; // Shift_JIS 字串(視環境而定)
    char result[20];

    substring_sjis(text, 5, 3, result);
    printf("子字串: %s\n", result);

    return 0;
}
重點
  • 若要正確處理 Shift_JIS,需設定 setlocale(LC_ALL, "Japanese");
  • 使用 mbstowcswcstombs,安全處理字串。

5.4 EUC-JP(多位元組字元)的情況

EUC-JP 的特性

EUC-JP 亦如同 Shift_JIS,因為每個字元的位元組數不同,需要使用寬字元進行轉換處理

支援 EUC-JP 的子字串取得

#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"); // 設定處理 EUC-JP 的 locale

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256);

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length);
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256);
}

int main() {
    char text[] = "こんにちは、世界!"; // EUC-JP 字串(視環境而定)
    char result[20];

    substring_eucjp(text, 5, 3, result);
    printf("子字串: %s\n", result);

    return 0;
}
重點
  • 使用 setlocale(LC_ALL, "ja_JP.eucJP"); 設定 EUC-JP 的 locale。
  • 使用 mbstowcs / wcstombs 正確處理多位元組字元。

5.5 總結

字元編碼位元組數建議的處理方法
ASCII1 位元組strncpy 可行
UTF-81~4 位元組使用 mbstowcs / wcstombs
Shift_JIS1 或 2 位元組使用 mbstowcs / wcstombs
EUC-JP1 或 2 位元組使用 mbstowcs / wcstombs
  • 如果僅是 ASCII 文字,strncpy 可行
  • 對於 UTF-8、Shift_JIS、EUC-JP,使用 mbstowcs / wcstombs
  • 依環境適當設定 setlocale(LC_ALL, "...");

6. C 語言中字串分割的方法

字串分割的處理在 CSV 資料的解析、命令列參數處理、日誌資料的解析 等多種情況下都是必需的。C 語言中,有使用 strtokstrtok_r 等標準函式庫函式的方法,也有自行撰寫函式的方法。 本節將詳細說明 以特定分隔字元分割字串的方法

6.1 使用 strtok 的字串分割

strtok 是一個以 指定的分隔字元(分隔符)分割字串 的函式。

基本語法

char *strtok(char *str, const char *delim);
  • str:要分割的字串(在第一次呼叫時指定)
  • delim:分隔字元(可指定多個)
  • 返回值:第一個 token(分割後的第一段)
  • 注意點strtok 會修改原始字串(將分隔字元改為 ' '

使用範例:以逗號 , 分割字串

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

int main() {
    char str[] = "apple,banana,orange,grape"; // 要分割的字串
    char *token = strtok(str, ","); // 取得第一個 token

    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok(NULL, ","); // 取得下一個 token
    }

    return 0;
}

執行結果

Token: apple
Token: banana
Token: orange
Token: grape

strtok 的注意點

  1. 會修改原始字串
  • strtok將分隔字元改寫為 ' ',因此原始字串會遺失。
  1. 不是執行緒安全的
  • strtok 在內部使用全域靜態變數,因此在多執行緒環境下不建議使用。

6.2 使用 strtok_r 的執行緒安全字串分割

strtok_rstrtok 的執行緒安全版,因為將狀態保存於 saveptr,所以在多執行緒環境中也能安全使用

基本語法

char *strtok_r(char *str, const char *delim, char **saveptr);
  • str:要分割的字串(在第一次呼叫時指定)
  • delim:分隔字元(可指定多個)
  • saveptr:保存內部狀態的指標(每次呼叫會更新)

使用範例:以空格分割字串

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

int main() {
    char str[] = "Hello World from C"; // 要分割的字串
    char *token;
    char *saveptr; // 保存內部狀態的指標

    token = strtok_r(str, " ", &saveptr); // 取得第一個 token
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok_r(NULL, " ", &saveptr); // 取得下一個 token
    }

    return 0;
}

strtok_r 的優點

  • 執行緒安全
  • 可同時處理多個字串

6.3 使用自製函式分割字串(不使用 strtok 的方法)

strtok 會修改原始字串,因此也可以建立不改變字串的自製函式來分割字串。

自製函式的規格

  • 輸入
  • const char *source(原始字串)
  • const char delim(分隔字元)
  • char tokens[][50](用來存放分割後字串的陣列)
  • 處理
  • 複製 source,以免修改原始字串
  • 根據 delim 將分割結果存入 tokens

實作程式碼

#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] != ' ') {
        if (source[i] == delim) {
            tokens[token_index][j] = ' ';
            token_index++;
            j = 0;
        } else {
            tokens[token_index][j] = source[i];
            j++;
        }
        i++;
    }
    tokens[token_index][j] = ' ';
    *count = token_index + 1;
}

int main() {
    char text[] = "dog,cat,bird,fish";
    char tokens[10][50]; // 最多可容納 10 個單詞
    int count;

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

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

    return 0;
}

執行結果

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

要點

  • source 不修改,先建立副本再處理。
  • 將分割結果存入 tokens 陣列,保留原始字串。

6.4 字串分割的應用(CSV 資料處理)

可以使用 strtok 解析 CSV(逗號分隔)資料。

CSV 資料解析範例

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

int main() {
    char csv[] = "Alice,24,Female\nBob,30,Male\nCharlie,28,Male"; // CSV 資料
    char *line = strtok(csv, "\n"); // 逐行處理

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

        printf("姓名: %s, 年齡: %s, 性別: %s\n", name, age, gender);

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

    return 0;
}

執行結果

姓名: Alice, 年齡: 24, 性別: Female
姓名: Bob, 年齡: 30, 性別: Male
姓名: Charlie, 年齡: 28, 性別: Male

6.5 總結

方法優點缺點
strtok可以簡單分割會修改原始字串
strtok_r執行緒安全使用方式稍微複雜
自製函式不會修改原始字串程式碼會變長
CSV 解析對資料處理很便利需注意 strtok 的限制

結論

  • 若是簡單分割則使用 strtok
  • 若是多執行緒則使用 strtok_r
  • 若不想修改原始字串則使用自製函式
  • 也可應用於 CSV 資料解析
接下來的章節將詳細說明「應用範例:抽取特定字元前後的方法」。

7. 應用範例:提取特定字元前後的方法

在字串處理時,常常需要 提取特定字元或關鍵字的前後 的操作。例如,可以考慮以下情況。
  • 僅取得 URL 的網域部分
  • 從檔案路徑中抽取檔名
  • 取得特定標籤或符號前後的字串
在 C 語言中,透過使用 strchrstrstr 可以實現此類處理。另外,若需要更彈性的處理,建立自訂函式也是有效的方法。

7.1 strchr 使用取得特定字元前的字串

strchr 可用於定位特定的字元(首次出現的)的位置。

基本語法

char *strchr(const char *str, int c);
  • str: 要搜尋的字串
  • c: 想要尋找的字元(char 型)
strchr 若找到 c,會回傳其位址。

使用範例:從檔案路徑取得檔名

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

void get_filename(const char *path, char *filename) {
    char *pos = strrchr(path, '/'); // 搜尋最後的 '/' 

    if (pos != NULL) {
        strcpy(filename, pos + 1); // '/' 的次的位址開始複製
    } else {
        strcpy(filename, path); // 若無 '/',直接複製
    }
}

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

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

    return 0;
}

執行結果

檔名: report.txt

要點

  • strrchr 可用於取得最後出現的特定字元(/)的位置
  • 使用 pos + 1 複製斜線之後的字串,即可僅取得檔名

7.2 strstr 使用取得特定關鍵字之後的字串

strstr 可用於搜尋特定字串(關鍵字),並取得其位置之後的字串

基本語法

char *strstr(const char *haystack, const char *needle);
  • haystack: 要搜尋的字串
  • needle: 要搜尋的子字串
strstr 若找到 needle,會回傳其位置的位址。

使用範例:從 URL 取得網域

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

void get_domain(const char *url, char *domain) {
    char *pos = strstr(url, "://"); // 搜尋 "://"

    if (pos != NULL) {
        strcpy(domain, pos + 3); // "://" 之後開始複製
    } else {
        strcpy(domain, url); // 若無 "://",直接複製
    }
}

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

    get_domain(url, domain);
    printf("網域部分: %s\n", domain);

    return 0;
}

執行結果

網域部分: www.example.com/page.html

要點

  • 使用 strstr 取得 "https://""http://" 中的 "//" 之後的部分。
  • 使用 pos + 3:// 之後開始複製。

7.3 strchr 使用分割特定字元前後的部分

利用 strchr,可以將特定字元前後的字串分割並取得

使用範例:從電子郵件地址分離使用者名稱與網域

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

void split_email(const char *email, char *username, char *domain) {
    char *pos = strchr(email, '@'); // 搜尋 '@' 的位置

    if (pos != NULL) {
        strncpy(username, email, pos - email); // '@' 之前的部分
        username[pos - email] = '\0'; // 加入 NUL 字元
        strcpy(domain, pos + 1); // '@' 之後的部分
    }
}

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

    split_email(email, username, domain);
    printf("使用者名稱: %s\n", username);
    printf("網域: %s\n", domain);

    return 0;
}

執行結果

使用者名稱: user
網域: example.com

要點

  • 使用 strchr 搜尋 '@' 的位置。
  • strncpy 用於複製 '@' 之前的部分,並加入 NUL 字元
  • 使用 strcpy 複製 '@' 之後的部分

7.4 應用:抽取 HTML 標籤內的特定屬性

在 HTML 標籤中取得特定屬性時,也可以利用 strstr

使用範例:從 <a href="URL"> 取得 URL

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

void get_href(const char *html, char *url) {
    char *start = strstr(html, "href="); // 搜尋 href=" 的位置
    if (start != NULL) {
        start += 6; // 移動到 href=" 之後
        char *end = strchr(start, '"'); // 搜尋下一個 "
        if (end != NULL) {
            strncpy(url, start, end - start);
            url[end - start] = '\0'; // 加入 NUL 字元
        }
    }
}

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

    get_href(html, url);
    printf("抽出的 URL: %s\n", url);

    return 0;
}

執行結果

抽出的 URL: https://example.com

要點

  • 使用 strstr 搜尋 "href=" 的位置,並移動 6 個字元。
  • 使用 strchr 搜尋 "(引號)的位址,以確定範圍。

7.5 小結

處理內容使用函式優點
取得特定字元前的部分strchr / strrchr簡單且高速
取得特定字元後的部分strstr可搜尋關鍵字
以特定字元分割前後strchr + strncpy對於使用者名稱、網域分割等很方便
取得 HTML 標籤屬性strstr + strchr可應用於網頁爬蟲

結論

  • strchrstrstr 的活用,可輕鬆取得特定字元或關鍵字的前後
  • 檔案路徑處理、URL 解析、電子郵件地址分割等,於多種情境中都有幫助
  • 亦可應用於如網頁爬蟲等高階處理

8. 總結

在本文中,C語言中字串的切割方法從基礎到應用都做了詳細說明。現在,回顧各章節的重要要點,並依用途整理最適方法。

8.1 文章回顧

章節內容重要要點
C語言字串的基礎在C語言中,字串被視為 char 陣列,終止字元 ' ' 很重要處理字串時,不要忘記空字元結尾
使用標準函式庫切割使用 strncpystrchrstrncpy 需要 手動加入空字元結尾
自訂函式切割建立彈性的 substring 函式使用 malloc取得可變長度的子字串
依文字編碼處理對 UTF-8、Shift_JIS、EUC-JP 的對應方法使用 mbstowcs / wcstombs 轉換為寬字元較安全
字串分割方法使用 strtokstrtok_r,或自訂函式分割strtok修改原始字串,需注意
抽取特定字元前後使用 strchrstrstr 取得資料可應用於 檔名取得、URL 解析、HTML 解析

8.2 依用途的最佳方法

1. 子字串切割

使用情境最佳方法
想取得固定長度的字串strncpysubstring()
想要安全的切割strncpy_s(C11 之後)
處理多位元組字元(UTF-8、Shift_JIS、EUC-JP)mbstowcs / wcstombs

2. 字串分割

使用情境最佳方法
想簡單分割字串strtok
想要執行緒安全的分割strtok_r
想在不修改原始字串的情況下分割自訂函式(split_string()

3. 取得特定字元前後

使用情境最佳方法
從檔案路徑取得檔名strrchr(path, '/')
從 URL 取得網域部分strstr(url, "://")
從電子郵件地址分離使用者名稱與網域strchr(email, '@')
從 HTML 標籤取得屬性值strstr(tag, "href="") + strchr(tag, '"')

8.3 C語言字串處理的注意事項

1. 徹底管理空字元 ' ' 的結尾

在 C 語言的字串處理中,適當管理終止字元 ' ' 是最重要的。特別是使用 strncpystrchr 時,請注意手動加入空字元
安全的字串複製範例
#include <stdio.h>
#include <string.h>

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

    strncpy(dest, src, 5);
    dest[5] = ' '; // 為了安全加入空字元結尾

    printf("子字串: %s
", dest);

    return 0;
}

2. 注意緩衝區溢位

在 C 語言的字串操作中,需要謹慎實作以避免存取陣列範圍外。特別是使用 strncpy 時,控制要複製的位元組數是很重要的。 安全的字串複製範例
#include <stdio.h>
#include <string.h>

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

    strncpy(dest, src, sizeof(dest) - 1);
    dest[5] = ' '; // 明確加入空字元

    printf("子字串: %s
", dest);
    return 0;
}

3. 多位元組字元處理使用 mbstowcs

在處理 UTF-8、Shift_JIS 等多位元組字元時,單純使用 strncpystrlen 會無法正確運作。 因此,處理多位元組字元時,建議先使用 mbstowcs 轉換為寬字元字串,再適當處理。
#include <stdio.h>
#include <wchar.h>
#include <locale.h>

int main() {
    setlocale(LC_ALL, ""); // 設定 locale

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

    mbstowcs(wtext, text, 256); // 轉換為寬字元字串

    printf("轉換後的寬字元字串: %ls
", wtext);

    return 0;
}

4. 緩衝區大小管理

在字串處理中,事先計算所需記憶體大小,並防止緩衝區溢位是很重要的。特別是使用 malloc 取得動態記憶體時,請正確掌握其大小。

8.4 進一步學習方向

C 語言的字串處理是提升程式安全性與可讀性的關鍵技能。基於本文介紹的內容,若再學習以下主題,將能進行更高階的字串處理。

深化學習的主題

  1. 正規表達(regex)(可透過 C 語言的外部函式庫實作)
  2. 檔案操作(使用 fgets、fscanf 進行字串處理)
  3. 記憶體管理(使用 malloc、realloc 進行動態字串處理)
  4. 資料解析(JSON、XML 的解析方法)

8.5 總結

  1. C 語言的字串以 char 陣列管理,故終止字元 ' ' 的處理很重要
  2. 子字串切割可使用 strncpysubstring()malloc
  3. 字串分割可活用 strtok / strtok_r / 自訂函式
  4. 取得特定字元前後時,可活用 strchrstrstr
  5. 處理多位元組字元(如日文)時,使用 mbstowcs
  6. 保持安全的字串處理,注意緩衝區溢位
若能活用本文內容,將能在 C 語言中實作實用的字串處理。在理解基本函式的基礎上,挑戰自訂函式與應用處理,寫出更有效率的程式碼吧!
侍エンジニア塾