首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言字串分割完全指南:sscanf、strstr 手寫切割、結構體封裝三種實作

     編輯:關於C語言

C語言字串分割完全指南:sscanf、strstr 手寫切割、結構體封裝三種實作

C 標準庫未內建 split 函式,開發者常需自行實作字串分割。本文整理三種常見做法:sscanf 格式化掃描、指標操作 strstr 手寫切割、結構體封裝動態陣列,並附上完整可編譯範例。

方法一:sscanf 格式化字串掃描(最簡潔)

#include <stdio.h>

int main() {
    char str1[10] = {0}, str2[10] = {0}, str3[10] = {0};
    /* %[^#] 表示讀取直到遇到 # 為止 */
    sscanf("1,2,3#3,4#4,5", "%[^#]#%[^#]#%[^#]", str1, str2, str3);
    printf("The split result is:\n%s\n%s\n%s\n", str1, str2, str3);
    return 0;
}
/* 輸出:
1,2,3
3,4
4,5
*/
  • 優點:程式碼極短,適合固定欄位、已知分隔符數量的情境。
  • 缺點:緩衝區大小固定,易溢位;分隔符需為單一字元;無法處理動態數量欄位。

方法二:strstr 手寫切割,回傳 char** 動態陣列


/* 實現方案 1:陣列第 0 格存數量 */
char** StringSplit(const char* string, const char* split) {
    char** result = (char**)malloc(sizeof(char*));
    memset(result, 0, sizeof(char*));
    char* p = (char*)string;
    char* pos = (char*)string;
    int count = 1;                 /* 至少會分割出 1 段 */

    while (*p != '\0') {
        pos = strstr(p, split);
        if (pos == NULL) {         /* 找不到分隔符,剩餘全部為最後一段 */
            result = (char**)realloc(result, sizeof(char*) * (count + 2));
            result[0] = (char*)count;
            result[count] = p;
            result[count + 1] = NULL;
            return result;
        }

        /* 分配暫存空間複製子字串 */
        char* temp = (char*)malloc(pos - p + 1);
        memset(temp, 0, pos - p + 1);
        char* head = temp;
        while (p < pos) *temp++ = *p++;
        *temp = '\0';

        result = (char**)realloc(result, sizeof(char*) * (count + 1));
        result[0] = (char*)count;
        result[count] = head;
        count++;

        /* 關鍵:跨過分隔符長度,避免重複匹配 */
        p += strlen(split);
    }
    return result;
}

測試程式


int main() {
    char** arr = StringSplit("a/aaa//哈aa", "aaa");
    for (int i = 1; i <= (int)arr[0]; i++)
        printf("Num:%d I:%d: Value: %s\n", (int)arr[0], i, arr[i]);
    /* 輸出:
    Num:2 I:1: Value: a/
    Num:2 I:2: Value: //哈aa
    */
    return 0;
}

方法三:結構體封裝 StringTab,語意更清晰


typedef struct {
    int number;      /* 分割段數 */
    char** string;   /* 字串陣列,索引 0 開始 */
} StringTab;

StringTab StringSplit_Struct(const char* string, const char* split) {
    StringTab result;
    result.string = (char**)malloc(sizeof(char*));
    memset(result.string, 0, sizeof(char*));
    result.number = 0;

    char* p = (char*)string;
    char* pos = (char*)string;

    while (*p != '\0') {
        pos = strstr(p, split);
        if (pos == NULL) {
            result.string = (char**)realloc(result.string,
                sizeof(char*) * (result.number + 1));
            result.string[result.number] = p;
            return result;
        }

        char* temp = (char*)malloc(pos - p + 1);
        memset(temp, 0, pos - p + 1);
        char* head = temp;
        while (p < pos) *temp++ = *p++;
        *temp = '\0';

        result.string = (char**)realloc(result.string,
            sizeof(char*) * (result.number + 1));
        result.string[result.number] = head;
        result.number++;

        p += strlen(split);
    }
    return result;
}

記憶體管理提醒

  • 所有 malloc/realloc 產生的記憶體,呼叫端必須自行 free 每一段子字串與陣列本身。
  • 若分隔符為多字元(如 "::"),p += strlen(split) 步進至關重要,否則會產生空字串或重複切割。
  • 生產環境建議加入錯誤檢查(malloc 失敗回傳 NULL)。

效能比較與選型建議

方法適用場景效能記憶體安全
sscanf固定欄位、簡單分隔符、原型開發高需自行限制緩衝區
strstr + char**動態欄位、多字元分隔符、C 風格 API中需完整釋放流程
StringTab 結構體大型專案、需封裝語意、配合其他模組中同左,結構體統一管理

若專案允許 C++,優先使用 std::string + std::vector 或 Boost.Tokenizer;純 C 環境下,方法二與三兼具彈性與可維護性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved