如何在C语言中对含符号的Unix命令进行正确分词?
解决strtok无法处理多字符分隔符的问题
嘿,我完全懂你的困扰!strtok()这个函数确实有个挺烦的局限性——它只会把分隔符参数里的每个单个字符都当成独立的分隔符,根本没法识别像" ("这种多字符的分隔序列。所以你哪怕只传"("当成分隔符,分割出来的令牌前后还是会带着空格,execvp()肯定不认这种带空格的参数。
针对你的需求,我给你几个实用的解决方案:
方案1:分割后对令牌做Trim处理(最简单直接)
既然strtok()分割出来的令牌只是带了前后空格,那我们可以写个小函数把这些空格去掉。这样不管分隔符是括号还是空格,处理完的令牌就干净了。
先写一个Trim函数,去掉字符串前后的空白字符:
char* trim(char *str) { // 跳过开头的空格 while (*str == ' ') str++; if (*str == '\0') return str; // 去掉结尾的空格 char *end = str + strlen(str) - 1; while (end > str && *end == ' ') end--; *(end + 1) = '\0'; return str; }
然后结合strtok()使用:
#include <stdio.h> #include <string.h> // 上面的trim函数放这里 int main() { char input[] = "ps aux ( sort ( more"; char *token = strtok(input, "("); char *tokens[10]; int token_count = 0; while (token != NULL) { char *clean_token = trim(token); // 跳过空字符串(比如连续分隔符的情况) if (strlen(clean_token) > 0) { tokens[token_count++] = clean_token; } token = strtok(NULL, "("); } // 如果需要把"ps aux"拆成"ps"和"aux"(给execvp用的正确格式),再分一次: char *final_tokens[20]; int final_count = 0; for (int i = 0; i < token_count; i++) { char *sub_token = strtok(tokens[i], " "); while (sub_token != NULL) { final_tokens[final_count++] = sub_token; sub_token = strtok(NULL, " "); } } // 现在final_tokens里就是["ps", "aux", "sort", "more"],可以直接给execvp用 for (int i = 0; i < final_count; i++) { printf("Token %d: '%s'\n", i, final_tokens[i]); } return 0; }
方案2:预处理字符串,替换多字符分隔符
如果你想严格把" ("当成分隔符,可以先遍历字符串,把所有" ("替换成一个不常用的单字符(比如'|'),然后再用strtok()分割。这样就能精准分割出你想要的令牌:
#include <stdio.h> #include <string.h> // 复用之前的trim函数 int main() { char input[] = "ps aux ( sort ( more"; char temp[256]; // 根据你的需求调整大小 int idx = 0; char *ptr = input; // 遍历字符串,替换" ("为'|' while (*ptr) { if (*ptr == ' ' && *(ptr + 1) == '(') { temp[idx++] = '|'; ptr += 2; // 跳过" ("两个字符 } else { temp[idx++] = *ptr++; } } temp[idx] = '\0'; // 现在用'|'作为分隔符分割 char *token = strtok(temp, "|"); while (token != NULL) { char *clean = trim(token); // 还是要trim一下,防止其他空格 if (strlen(clean) > 0) { printf("Clean token: '%s'\n", clean); } token = strtok(NULL, "|"); } return 0; }
方案3:用正则表达式匹配令牌(更灵活)
如果你的分隔规则更复杂,正则表达式会是更好的选择。用regcomp()和regexec()可以直接匹配出所有符合要求的令牌:
#include <stdio.h> #include <regex.h> #include <string.h> #include <stdlib.h> // 复用之前的trim函数 int main() { char input[] = "ps aux ( sort ( more"; regex_t regex; regmatch_t matches[2]; // 正则表达式:匹配所有非'('的字符序列 const char *pattern = "[^(]+"; if (regcomp(®ex, pattern, REG_EXTENDED) != 0) { fprintf(stderr, "正则表达式编译失败\n"); return 1; } char *ptr = input; while (regexec(®ex, ptr, 2, matches, 0) == 0) { int len = matches[0].rm_eo - matches[0].rm_so; char *token = malloc(len + 1); strncpy(token, ptr + matches[0].rm_so, len); token[len] = '\0'; char *clean = trim(token); if (strlen(clean) > 0) { printf("Token: '%s'\n", clean); } ptr += matches[0].rm_eo; free(token); } regfree(®ex); return 0; }
总结
strtok()确实不支持多字符分隔符,但通过预处理、Trim处理或者正则表达式,完全能解决你的问题。如果只是简单的情况,方案1的Trim处理最省心;如果分隔规则很明确,方案2的替换法也很高效;复杂场景就用正则表达式。
内容的提问来源于stack exchange,提问作者Doughey
相关产品推荐
相关产品推荐

