有什么办法可以将带有分隔符的 CONST char * 拆分成数组吗?

Is there any way to split a CONST char * with a delimiter into an array?

我正在尝试将给定的字符串(输入)拆分为一个元素数组。这是我的代码:

char *buff = save_to_buff(); // save the input
int token_count = 1;
for(int i = 0; buff[i] != '[=10=]';i++)
{
    if(buff[i] == ' ')
    {
        token_count++;
    }
}
char *token = strtok(buff, " ");
char *arr[token_count];

for(int i = 0;token != NULL;i++)
{
    arr[i] = token;
    token = strtok(NULL, " ");
}
for(int i = 0; i < token_count;i++)
{
    printf("%s ", arr[i]);
}

它有效,但是我需要创建一个函数 char **parse_cmdline(const char *cmdline) 将这种情况下的 buff(cmdline) 拆分为一个数组,但是如果甚至有可能吗?我要么收到 'const' 限定符被丢弃的警告,要么收到错误。有什么办法吗?

const 个对象无法修改。这是未定义的行为。在使用 strtok.

之前,您需要制作可修改的字符串副本
char **split(const char *restrict str, const char *restrict delim)
{
    char **result = NULL;
    char *copy;
    size_t ntokensLen;
    if(str && delim && *str && *delim)
    {
        copy = malloc(ntokensLen = strlen(str + 1));
        if(copy)
        {
            char *token;
            
            memcpy(copy, str, ntokensLen + 1);
            ntokensLen = 0;
            token = strtok(copy, delim);

            if(!token) free(copy); 
   
            while(token) 
            {   
                char **tmp;
                tmp = realloc(result, (ntokensLen + 2) * sizeof(*tmp));
                if(!tmp) { /* error hanling */}
                result = tmp;
                result[ntokensLen] = token;
                result[ntokensLen + 1] = NULL;
                token = strtok(NULL, delim);
                ntokensLen++;
            }
        }
    }
    return result;
}

int main(void)
{
    const char *str = "This!is string ^to test...";

    char **result = split(str, "! ^.");
    size_t cnt = 0;

    while(result[cnt])
    {
        printf("result[%zu] = `%s`\n", cnt, result[cnt]);
        cnt++;
    }
    // how to free? 
    free(result[0]);
    free(result);
}

编辑:

添加了如何免费。 result 保存对 realloced 内存的引用,result[0]malloced.

result 是 NULL 指针终止。

其他拆分版本:

char **mystrtok(const char *str, const char *del, int alowempty)
{
  char **result = NULL;
  const char *end = str;
  size_t size = 0;
  int extrachar;

  while(*end)
  {
    if((extrachar = !!strchr(del, *end)) || !*(end + 1))
    {
        /* add temp variable and malloc / realloc checks */
        /* free allocated memory on error */
        if(!(!alowempty && !(end - str)))
        {
            extrachar = !extrachar * !*(end + 1);
            result = realloc(result, (++size + 1) * sizeof(*result));
            result[size] = NULL;
            result[size -1] = malloc(end - str + 1 + extrachar);
            strncpy(result[size -1], str, end - str + extrachar);
            result[size -1][end - str + extrachar] = 0;
        }
        str = end + 1;
    }
    end++;
  }
  return result;
}

调用者提供的双指针

char **split(char **argv, int *argc, const char *str, const char *delimiter, int allowempty)
{
    char *string = malloc(strlen(str + 1));
    strcpy(string, str);
    *argc = 0;
    do
    {
        if(*string && (!strchr(delimiter, *string) || allowempty))
        {
            argv[(*argc)++] = string;
        }
        while(*string && !strchr(delimiter, *string)) string++;
        if(*string) *string++ = 0;
        if(!allowempty) 
            while(*string && strchr(delimiter, *string)) string++;
    }while(*string);
    return argv;
}

您可以将函数拆分为两个函数。

第一个将 return 给定字符串中的标记数。使用函数的 return 值,您可以分配一个指针数组,其元素数等于给定字符串中的标记数加一。那就是令牌数组将以空指针结尾。

第二个函数将用给定字符串的标记填充提供的数组。

这里有一个演示程序。

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

size_t count_tokens( const char *s1, const char *s2 )
{
    size_t n = 0;

    while (*s1)
    {
        s1 += strspn( s1, s2 );

        if (*s1)
        {
            ++n;
            s1 += strcspn( s1, s2 );
        }
    }

    return n;
}

size_t get_tokens( char **s1, const char *s2, const char *s3 )
{
    size_t n = 0;

    while (*s2)
    {
        s2 += strspn( s2, s3 );
        
        if (*s2)
        {
            ++n;

            const char *p = s2;
            s2 += strcspn( s2, s3 );

            size_t len = s2 - p;
            *s1 = malloc( len + 1 );

            if (*s1)
            {
                memcpy( *s1, p, len );
                ( *s1 )[len] = '[=10=]';
            }

            ++s1;
        }
    }

    *s1 = NULL;

    return n;
}


int main( void )
{
    const char *s1 = "Hello World!";

    size_t n = count_tokens( s1, " " );

    printf( "%zu\n", n );

    char **p = malloc( ( n + 1 ) * sizeof( char * ) );

    get_tokens( p, s1, " " );

    for ( size_t i = 0; i < n; i++ )
    {
        if ( p[i] ) puts( p[i] );
    }

    for (size_t i = 0; i < n; i++)
    {
        free( p[i] );
    }

    free( p );
}

程序输出为

2
Hello
World!

作为标记的分隔符,您可以将任何字符串传递给函数,例如 " \t\n'