在C中拆分带有多字符分隔符的char字符串

时间:2015-04-22 06:02:22

标签: c string parsing tokenize delimiter

我想根据多字符分隔符拆分char *string。我知道strtok()用于分割字符串,但它适用于单字符分隔符。

我想基于诸如"abc"之类的子字符串或任何其他子字符串来拆分char * string。如何实现?

4 个答案:

答案 0 :(得分:3)

找到所需序列发生的位置非常简单:strstr支持:

char str[] = "this is abc a big abc input string abc to split up";
char *pos = strstr(str, "abc");

因此,在此时,pos指向较大字符串中abc的第一个位置。事情变得有点难看。 strtok有一个令人讨厌的设计,它1)修改原始字符串,2)在内部存储指向字符串中“当前”位置的指针。

如果我们不介意大致相同,我们可以这样做:

char *multi_tok(char *input, char *delimiter) {
    static char *string;
    if (input != NULL)
        string = input;

    if (string == NULL)
        return string;

    char *end = strstr(string, delimiter);
    if (end == NULL) {
        char *temp = string;
        string = NULL;
        return temp;
    }

    char *temp = string;

    *end = '\0';
    string = end + strlen(delimiter);
    return temp;
}

这确实有效。例如:

int main() {
    char input [] = "this is abc a big abc input string abc to split up";

    char *token = multi_tok(input, "abc");

    while (token != NULL) {
        printf("%s\n", token);
        token = multi_tok(NULL, "abc");
    }
}

产生大致预期的产出:

this is
 a big
 input string
 to split up

尽管如此,它是笨拙的,难以使线程安全(你必须使其内部string变量线程本地)并且通常只是一个糟糕的设计。使用(例如)类似strtok_r的接口,我们至少可以解决线程安全问题:

typedef char *multi_tok_t;

char *multi_tok(char *input, multi_tok_t *string, char *delimiter) {
    if (input != NULL)
        *string = input;

    if (*string == NULL)
        return *string;

    char *end = strstr(*string, delimiter);
    if (end == NULL) {
        char *temp = *string;
        *string = NULL;
        return temp;
    }

    char *temp = *string;

    *end = '\0';
    *string = end + strlen(delimiter);
    return temp;
}

multi_tok_t init() { return NULL; }

int main() {
    multi_tok_t s=init();

    char input [] = "this is abc a big abc input string abc to split up";

    char *token = multi_tok(input, &s, "abc");

    while (token != NULL) {
        printf("%s\n", token);
        token = multi_tok(NULL, &s, "abc");
    }
}

我想我现在就把它留在那里 - 为了得到一个非常干净的界面,我们真的想重新发明类似协程的东西,这可能有点多发布在这里。

答案 1 :(得分:1)

您可以使用strstr()轻松编写自己的解析器来实现相同的目标。基本算法可能如下所示

  • 使用strstr()查找整个分隔符字符串的第一个匹配项
  • 标记索引
  • 从开始复制到标记的索引,这将是您预期的令牌。
  • 解析后续条目的输入,调整初始字符串的指示以按标记长度+分隔符字符串的长度前进。

答案 2 :(得分:1)

编辑:考虑了Alan和Sourav的建议,并为此编写了基本代码。

#include <stdio.h>

#include <string.h>

int main (void)
{
  char str[] = "This is abc test abc string";

  char* in = str;
  char *delim = "abc";
  char *token;

  do {

    token = strstr(in,delim);

    if (token) 
      *token = '\0';

    printf("%s\n",in);

    in = token+strlen(delim);

  }while(token!=NULL);


  return 0;
}

答案 3 :(得分:0)

我写了一个线程安全的简单实现:

struct split_string {
    int len;
    char** str;
};
typedef struct split_string splitstr;
splitstr* split(char* string, char* delimiter) {
    int targetsize = 0;
    splitstr* ret = malloc(sizeof(splitstr));
    if (ret == NULL)
        return NULL;
    ret->str = NULL;
    ret->len = 0;
    char* pos;
    char* oldpos = string;
    int newsize;
    int dlen = strlen(delimiter);
    do {
        pos = strstr(oldpos, delimiter);
        if (pos) {
            newsize = pos - oldpos;
        } else {
            newsize = strlen(oldpos);
        }
        char* newstr = malloc(sizeof(char) * (newsize + 1));
        strncpy(newstr, oldpos, newsize);
        newstr[newsize] = '\0';
        oldpos = pos + dlen;
        ret->str = realloc(ret->str, (targetsize+1) * sizeof(char*));
        ret->str[targetsize++] = newstr;
        ret->len++;
    } while (pos != NULL);
    return ret;
}

使用:

splitstr* ret = split(contents, "\n");
for (int i = 0; i < ret->len; i++) {
    printf("Element %d: %s\n", i, ret->str[i]);
}