You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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(&regex, pattern, REG_EXTENDED) != 0) {
        fprintf(stderr, "正则表达式编译失败\n");
        return 1;
    }

    char *ptr = input;
    while (regexec(&regex, 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(&regex);
    return 0;
}

总结

strtok()确实不支持多字符分隔符,但通过预处理、Trim处理或者正则表达式,完全能解决你的问题。如果只是简单的情况,方案1的Trim处理最省心;如果分隔规则很明确,方案2的替换法也很高效;复杂场景就用正则表达式。

内容的提问来源于stack exchange,提问作者Doughey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:18:14