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

Time:12-26

I'm trying to split a given string (input) into an array of elements. Here is my code:

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

It works, however I need to make a function char **parse_cmdline(const char *cmdline) which splits the buff(cmdline) in this case into an array, but how can I do that if it is even possible? I either get a warning that the 'const' qualifier is discared or an error. Is there any way?

CodePudding user response:

You can split the function into two functions.

The first one will return the number of tokens in a given string. Using the return value of the function you can allocate an array of pointers the number of elements of which is equal to the number of tokens in the given string plus one. That is the array of tokens will end with a null pointer.

The second function will fill the provided array with tokens of the given string.

Here is a demonstration program.

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

The program output is

2
Hello
World!

As a delimiter of tokens you can pass any string to the functions as for example " \t\n'.

CodePudding user response:

const objects cannot be modified. It is undefined behaviour. You need to make a modifiable copy of the string before you use 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);
}

EDIT:

Added How to free. result holds the reference to realloced memory, result[0] to malloced.

result is NULL pointer terminated.

Other split versions:

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

double pointer supplied by the caller

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