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 環境下,方法二與三兼具彈性與可維護性。