在C中拆分带分隔符的字符串

nam*_*mco 148 c string split

如何使用C编程语言编写一个函数来拆分和返回带有分隔符的字符串数组?

char* str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
str_split(str,',');
Run Code Online (Sandbox Code Playgroud)

hmj*_*mjd 156

您可以使用该strtok()函数拆分字符串(并指定要使用的分隔符).请注意,strtok()将修改传递给它的字符串.如果其他地方需要原始字符串,请复制它并将副本传递给strtok().

编辑:

示例(注意它不处理连续的分隔符,例如"JAN ,,, FEB,MAR"):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

char** str_split(char* a_str, const char a_delim)
{
    char** result    = 0;
    size_t count     = 0;
    char* tmp        = a_str;
    char* last_comma = 0;
    char delim[2];
    delim[0] = a_delim;
    delim[1] = 0;

    /* Count how many elements will be extracted. */
    while (*tmp)
    {
        if (a_delim == *tmp)
        {
            count++;
            last_comma = tmp;
        }
        tmp++;
    }

    /* Add space for trailing token. */
    count += last_comma < (a_str + strlen(a_str) - 1);

    /* Add space for terminating null string so caller
       knows where the list of returned strings ends. */
    count++;

    result = malloc(sizeof(char*) * count);

    if (result)
    {
        size_t idx  = 0;
        char* token = strtok(a_str, delim);

        while (token)
        {
            assert(idx < count);
            *(result + idx++) = strdup(token);
            token = strtok(0, delim);
        }
        assert(idx == count - 1);
        *(result + idx) = 0;
    }

    return result;
}

int main()
{
    char months[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
    char** tokens;

    printf("months=[%s]\n\n", months);

    tokens = str_split(months, ',');

    if (tokens)
    {
        int i;
        for (i = 0; *(tokens + i); i++)
        {
            printf("month=[%s]\n", *(tokens + i));
            free(*(tokens + i));
        }
        printf("\n");
        free(tokens);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

$ ./main.exe
months=[JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC]

month=[JAN]
month=[FEB]
month=[MAR]
month=[APR]
month=[MAY]
month=[JUN]
month=[JUL]
month=[AUG]
month=[SEP]
month=[OCT]
month=[NOV]
month=[DEC]
Run Code Online (Sandbox Code Playgroud)

  • 嗨!`strtok`在手册页中被[`strsep(3)`](http://www.manpagez.com/man/3/strsep/)标记为已废弃. (57认同)
  • 因为这可能是关于Stack Overflow的规范问题/答案,对于使用strtok的多线程有没有一些注意事项? (4认同)
  • @Dojo:它记得它; 这是问题的原因之一.最好使用`strtok_s()`(Microsoft,C11 Annex K,可选)或`strtok_r()`(POSIX)而不是普通的`strtok()`.简单的`strtok()`在库函数中是邪恶的.调用库函数的函数当时没有使用`strtok()`,库函数调用的函数也不能调用`strtok()`. (4认同)
  • @osgx根据该页面,`strsep`是`strtok`的替代品,但`strtok`是可移植性的首选.因此,除非您需要支持空字段或一次拆分多个字符串,否则`strtok`是更好的选择. (3认同)
  • 只是注意`strtok()`不是线程安全的(因为@JonathanLeffler提到的原因)因此整个函数不是线程安全的.如果您尝试在交叉环境中使用它,您将获得不稳定和不可预测的结果.替换`strtok()`for`strtok_r()`解决了这个问题. (3认同)
  • 你好。我认为该函数已硬编码“,”作为分隔符: char* token = strtok(a_str, ","); (2认同)

Tyl*_*ler 67

我认为strsep仍然是最好的工具:

while ((token = strsep(&str, ","))) my_fn(token);
Run Code Online (Sandbox Code Playgroud)

这实际上是一行分割字符串.

额外的括号是一个风格元素,表示我们有意测试赋值的结果,而不是相等运算符==.

对于该模式的工作,token并且str都有型char *.如果你以字符串文字开头,那么你首先需要复制它:

// More general pattern:
const char *my_str_literal = "JAN,FEB,MAR";
char *token, *str, *tofree;

tofree = str = strdup(my_str_literal);  // We own str's memory now.
while ((token = strsep(&str, ","))) my_fn(token);
free(tofree);
Run Code Online (Sandbox Code Playgroud)

如果两个分隔符一起出现str,则会得到一个token空字符串的值.str修改的值是因为遇到的每个分隔符都被零字节覆盖 - 这是另一个复制首先被解析的字符串的好理由.

在评论中,有人建议这strtokstrsep因为strtok更便携更好.Ubuntu和Mac OS X有strsep; 可以安全地猜测其他unixy系统也能做到.Windows缺乏strsep,但它有strbrk这个简短而甜蜜的strsep替代品:

char *strsep(char **stringp, const char *delim) {
  if (*stringp == NULL) { return NULL; }
  char *token_start = *stringp;
  *stringp = strpbrk(token_start, delim);
  if (*stringp) {
    **stringp = '\0';
    (*stringp)++;
  }
  return token_start;
}
Run Code Online (Sandbox Code Playgroud)

是对strsepvs的一个很好的解释strtok.可以主观判断利弊; 但是,我认为这是一个明显的标志,strsep被设计为替代strtok.

  • 更准确地说是可移植性:它不是[不是POSIX 7](http://pubs.opengroup.org/onlinepubs/9699919799/functions/contents.html),而是BSD派生,并在[glibc]上实现(http:// www .gnu.org /软件/ libc的/手动/ html_node /查找-令牌-IN-A-String.html#查找-令牌功能于一个字符串). (3认同)
  • 为什么“tofree”是被释放的而不是“str”? (2认同)
  • 您无法释放“str”,因为它的值可以通过调用“strsep()”来更改。“tofree”的值始终指向要释放的内存的开头。 (2认同)

the*_*imp 25

String tokenizer这段代码应该让你朝着正确的方向前进.

int main(void) {
  char st[] ="Where there is will, there is a way.";
  char *ch;
  ch = strtok(st, " ");
  while (ch != NULL) {
  printf("%s\n", ch);
  ch = strtok(NULL, " ,");
  }
  getch();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)


use*_*944 12

下面的方法将为您完成所有工作(内存分配,计算长度).可以在这里找到更多信息和描述 - 实现Java String.split()方法来拆分C字符串

int split (const char *str, char c, char ***arr)
{
    int count = 1;
    int token_len = 1;
    int i = 0;
    char *p;
    char *t;

    p = str;
    while (*p != '\0')
    {
        if (*p == c)
            count++;
        p++;
    }

    *arr = (char**) malloc(sizeof(char*) * count);
    if (*arr == NULL)
        exit(1);

    p = str;
    while (*p != '\0')
    {
        if (*p == c)
        {
            (*arr)[i] = (char*) malloc( sizeof(char) * token_len );
            if ((*arr)[i] == NULL)
                exit(1);

            token_len = 0;
            i++;
        }
        p++;
        token_len++;
    }
    (*arr)[i] = (char*) malloc( sizeof(char) * token_len );
    if ((*arr)[i] == NULL)
        exit(1);

    i = 0;
    p = str;
    t = ((*arr)[i]);
    while (*p != '\0')
    {
        if (*p != c && *p != '\0')
        {
            *t = *p;
            t++;
        }
        else
        {
            *t = '\0';
            i++;
            t = ((*arr)[i]);
        }
        p++;
    }

    return count;
}
Run Code Online (Sandbox Code Playgroud)

如何使用它:

int main (int argc, char ** argv)
{
    int i;
    char *s = "Hello, this is a test module for the string splitting.";
    int c = 0;
    char **arr = NULL;

    c = split(s, ' ', &arr);

    printf("found %d tokens.\n", c);

    for (i = 0; i < c; i++)
        printf("string #%d: %s\n", i, arr[i]);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 这个例子有多个内存泄漏。对于阅读本文的任何人,请不要使用这种方法。更喜欢 strtok 或 strsep 标记化方法。 (6认同)
  • 嗯三星程序员:))这听起来很有趣. (4认同)

raz*_*zak 7

Here is my two cents:

int split (const char *txt, char delim, char ***tokens)
{
    int *tklen, *t, count = 1;
    char **arr, *p = (char *) txt;

    while (*p != '\0') if (*p++ == delim) count += 1;
    t = tklen = calloc (count, sizeof (int));
    for (p = (char *) txt; *p != '\0'; p++) *p == delim ? *t++ : (*t)++;
    *tokens = arr = malloc (count * sizeof (char *));
    t = tklen;
    p = *arr++ = calloc (*(t++) + 1, sizeof (char *));
    while (*txt != '\0')
    {
        if (*txt == delim)
        {
            p = *arr++ = calloc (*(t++) + 1, sizeof (char *));
            txt++;
        }
        else *p++ = *txt++;
    }
    free (tklen);
    return count;
}
Run Code Online (Sandbox Code Playgroud)

Usage:

char **tokens;
int count, i;
const char *str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";

count = split (str, ',', &tokens);
for (i = 0; i < count; i++) printf ("%s\n", tokens[i]);

/* freeing tokens */
for (i = 0; i < count; i++) free (tokens[i]);
free (tokens);
Run Code Online (Sandbox Code Playgroud)

  • 哦,Boi,三个指针!我已经害怕使用它了,只是我,我对c中的指针不是很好。 (2认同)