【问题标题】:Is there any way to split a CONST char * with a delimiter into an array?有没有办法将带有分隔符的 CONST char * 拆分为数组?
【发布时间】:2021-12-25 16:59:00
【问题描述】:

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

char *buff = save_to_buff(); // save the input
int token_count = 1;
for(int i = 0; buff[i] != '\0';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 限定的。您是否使用了正确的 main 声明?
  • @Neil 我正在使用“int main(int argc, char *argv[])”
  • @User_Not_Found 如果您将我的答案标记为最佳答案,我可以建议一个解决方案。:)
  • 是的,这是正确的,但是您在 parse_cmdline 中添加了常量。由于您显式修改了参数,因此将其设为const 没有意义。放下它。
  • @User_Not_Found 您选择了错误的方法。:)

标签: c split c-strings function-definition


【解决方案1】:

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

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

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

这是一个演示程序。

#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] = '\0';
            }

            ++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'

【讨论】:

  • 所以我实际上并没有使用 char **parse_cmdline(const char *cmdline) 而是将其功能拆分为其他两个功能?
  • @User_Not_Found 是的,最好把函数拆分成两个函数。当函数的用户传递带有分隔符的字符串时,它也更加灵活。
  • @User_Not_Found 你可以随意命名函数。
  • 好的,谢谢,但是用这种方法有什么办法可以使“char **parse_cmdline(const char *cmdline)”成为第三个函数或其他什么,因为我认为我正在使用的分级机检查函数本身是否存在,并可能在第三个函数中组合这两个函数而不是 main()?
  • @User_Not_Found 我不知道什么是“grader”但是这个函数声明 char **parse_cmdline(const char *cmdline) 不好。例如,它不接受带有分隔符的字符串。此外,不知道传递的字符串有多少令牌。不过,您可以将两个显示的函数组合为一个函数,该函数首先确定令牌的数量,然后在其内部分配一个指针数组。
【解决方案2】:

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;
}

【讨论】:

  • 有效!非常感谢!
  • 你如何释放copy
  • 由于内存泄漏,如何释放 tmp 和复制?
  • 如果 realloc 没问题,您不必将其分配给结果。如果不是,结果和以前一样。你只需要释放函数的返回值。无泄漏
  • 我试图在 main() 中释放结果,但是当我对程序进行 valgrind 时,它发现函数本身使用 malloc 和 realloc 存在 2 个内存泄漏
猜你喜欢
  • 2017-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 2011-08-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多