Подтвердить что ты не робот

Разделить строку с разделителями на C

Как написать функцию для разделения и возврата массива для строки с разделителями на языке программирования C?

char* str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
str_split(str,',');
4b9b3361

Ответ 1

Вы можете использовать функцию strtok() для разделения строки (и указать используемый разделитель). Обратите внимание, что strtok() изменит строку, переданную в нее. Если исходная строка требуется в другом месте, сделайте ее копию и передайте ее в strtok().

EDIT:

Пример (обратите внимание, что он не обрабатывает последовательные разделители, например, 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;
}

Вывод:

$ ./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]

Ответ 2

Я думаю, что strsep по-прежнему является лучшим инструментом для этого:

while ((token = strsep(&str, ","))) my_fn(token);

Это буквально одна строка, которая разбивает строку.

Дополнительные круглые скобки являются стилистическим элементом, указывающим, что мы намеренно проверяем результат присваивания, а не оператор равенства ==.

Для этого шаблона 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 memory now.
while ((token = strsep(&str, ","))) my_fn(token);
free(tofree);

Если два разделителя отображаются вместе в str, вы получите значение token, которое содержит пустую строку. Значение str изменяется в том, что каждый встреченный разделитель перезаписывается нулевым байтом - еще одна веская причина для копирования сначала строки, обрабатываемой.

В комментарии кто-то предположил, что strtok лучше, чем strsep, потому что 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;
}

Здесь является хорошим объяснением strsep vs strtok. Плюсы и минусы можно судить субъективно; однако, я считаю, что это знак, что strsep был создан как замена для strtok.

Ответ 3

Указатель строк, этот код должен поместить вас в правильном направлении.

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

Ответ 4

Метод ниже сделает всю работу (выделение памяти, подсчет длины) за вас. Дополнительную информацию и описание можно найти здесь - Реализация метода 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;
}

Как его использовать:

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

Ответ 5

Вот мои два цента:

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

Применение:

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

Ответ 6

В приведенном выше примере был бы способ вернуть массив нулевых завершенных строк (например, вы хотите) на место в строке. Это не дало бы возможность передавать литеральную строку, поскольку она должна была быть изменена функцией:

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

char** str_split( char* str, char delim, int* numSplits )
{
    char** ret;
    int retLen;
    char* c;

    if ( ( str == NULL ) ||
        ( delim == '\0' ) )
    {
        /* Either of those will cause problems */
        ret = NULL;
        retLen = -1;
    }
    else
    {
        retLen = 0;
        c = str;

        /* Pre-calculate number of elements */
        do
        {
            if ( *c == delim )
            {
                retLen++;
            }

            c++;
        } while ( *c != '\0' );

        ret = malloc( ( retLen + 1 ) * sizeof( *ret ) );
        ret[retLen] = NULL;

        c = str;
        retLen = 1;
        ret[0] = str;

        do
        {
            if ( *c == delim )
            {
                ret[retLen++] = &c[1];
                *c = '\0';
            }

            c++;
        } while ( *c != '\0' );
    }

    if ( numSplits != NULL )
    {
        *numSplits = retLen;
    }

    return ret;
}

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

    char* strCpy;
    char** split;
    int num;
    int i;

    strCpy = malloc( strlen( str ) * sizeof( *strCpy ) );
    strcpy( strCpy, str );

    split = str_split( strCpy, ',', &num );

    if ( split == NULL )
    {
        puts( "str_split returned NULL" );
    }
    else
    {
        printf( "%i Results: \n", num );

        for ( i = 0; i < num; i++ )
        {
            puts( split[i] );
        }
    }

    free( split );
    free( strCpy );

    return 0;
}

Возможно, существует более аккуратный способ сделать это, но вы получите эту идею.

Ответ 7

Эта функция принимает строку char * и разбивает ее на разделитель. В строке может быть несколько разделителей. Обратите внимание, что функция изменяет строку orignal. Сначала вы должны сделать копию исходной строки, если вам нужно, чтобы оригинал оставался неизменным. Эта функция не использует вызовы функций cstring, поэтому она может быть немного быстрее других. Если вам не важно распределение памяти, вы можете выделить sub_strings в верхней части функции с размером strlen (src_str)/2 и (например, упомянутая версия С++) пропустить нижнюю половину функции. Если вы это сделаете, функция сведена к O (N), но способ оптимизации памяти, показанный ниже, - O (2N).

Функция:

char** str_split(char *src_str, const char deliminator, size_t &num_sub_str){
  //replace deliminator with zeros and count how many
  //sub strings with length >= 1 exist
  num_sub_str = 0;
  char *src_str_tmp = src_str;
  bool found_delim = true;
  while(*src_str_tmp){
    if(*src_str_tmp == deliminator){
      *src_str_tmp = 0;
      found_delim = true;
    }
    else if(found_delim){ //found first character of a new string
      num_sub_str++;
      found_delim = false;
      //sub_str_vec.push_back(src_str_tmp); //for c++
    }
    src_str_tmp++;
  }
  printf("Start - found %d sub strings\n", num_sub_str);
  if(num_sub_str <= 0){
    printf("str_split() - no substrings were found\n");
    return(0);
  }

  //if you want to use a c++ vector and push onto it, the rest of this function
  //can be omitted (obviously modifying input parameters to take a vector, etc)

  char **sub_strings = (char **)malloc( (sizeof(char*) * num_sub_str) + 1);
  const char *src_str_terminator = src_str_tmp;
  src_str_tmp = src_str;
  bool found_null = true;
  size_t idx = 0;
  while(src_str_tmp < src_str_terminator){
    if(!*src_str_tmp) //found a NULL
      found_null = true;
    else if(found_null){
      sub_strings[idx++] = src_str_tmp;
      //printf("sub_string_%d: [%s]\n", idx-1, sub_strings[idx-1]);
      found_null = false;
    }
    src_str_tmp++;
  }
  sub_strings[num_sub_str] = NULL;

  return(sub_strings);
}

Как его использовать:

  char months[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
  char *str = strdup(months);
  size_t num_sub_str;
  char **sub_strings = str_split(str, ',', num_sub_str);
  char *endptr;
  if(sub_strings){
    for(int i = 0; sub_strings[i]; i++)
      printf("[%s]\n", sub_strings[i]);
  }
  free(sub_strings);
  free(str);

Ответ 8

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

/**
 *  splits str on delim and dynamically allocates an array of pointers.
 *
 *  On error -1 is returned, check errno
 *  On success size of array is returned, which may be 0 on an empty string
 *  or 1 if no delim was found.  
 *
 *  You could rewrite this to return the char ** array instead and upon NULL
 *  know it an allocation problem but I did the triple array here.  Note that
 *  upon the hitting two delim in a row "foo,,bar" the array would be:
 *  { "foo", NULL, "bar" } 
 * 
 *  You need to define the semantics of a trailing delim Like "foo," is that a
 *  2 count array or an array of one?  I choose the two count with the second entry
 *  set to NULL since it valueless.
 *  Modifies str so make a copy if this is a problem
 */
int split( char * str, char delim, char ***array, int *length ) {
  char *p;
  char **res;
  int count=0;
  int k=0;

  p = str;
  // Count occurance of delim in string
  while( (p=strchr(p,delim)) != NULL ) {
    *p = 0; // Null terminate the deliminator.
    p++; // Skip past our new null
    count++;
  }

  // allocate dynamic array
  res = calloc( 1, count * sizeof(char *));
  if( !res ) return -1;

  p = str;
  for( k=0; k<count; k++ ){
    if( *p ) res[k] = p;  // Copy start of string
    p = strchr(p, 0 );    // Look for next null
    p++; // Start of next string
  }

  *array = res;
  *length = count;

  return 0;
}

char str[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,";

int main() {
  char **res;
  int k=0;
  int count =0;
  int rc;

  rc = split( str, ',', &res, &count );
  if( rc ) {
    printf("Error: %s errno: %d \n", strerror(errno), errno);
  }

  printf("count: %d\n", count );
  for( k=0; k<count; k++ ) {
    printf("str: %s\n", res[k]);
  }

  free(res );
  return 0;
}

Ответ 9

Ниже приведена моя реализация strtok() из библиотеки zString. zstring_strtok() отличается от стандартной библиотеки strtok() тем, как она обрабатывает последовательные разделители.

Просто взгляните на приведенный ниже код, убедитесь, что вы получите представление о том, как это работает (я пытался использовать столько комментариев, сколько мог)

char *zstring_strtok(char *str, const char *delim) {
    static char *static_str=0;      /* var to store last address */
    int index=0, strlength=0;       /* integers for indexes */
    int found = 0;                  /* check if delim is found */

    /* delimiter cannot be NULL
    * if no more char left, return NULL as well
    */
    if (delim==0 || (str == 0 && static_str == 0))
        return 0;

    if (str == 0)
        str = static_str;

    /* get length of string */
    while(str[strlength])
        strlength++;

    /* find the first occurance of delim */
    for (index=0;index<strlength;index++)
        if (str[index]==delim[0]) {
            found=1;
            break;
        }

    /* if delim is not contained in str, return str */
    if (!found) {
        static_str = 0;
        return str;
    }

    /* check for consecutive delimiters
    *if first char is delim, return delim
    */
    if (str[0]==delim[0]) {
        static_str = (str + 1);
        return (char *)delim;
    }

    /* terminate the string
    * this assignmetn requires char[], so str has to
    * be char[] rather than *char
    */
    str[index] = '\0';

    /* save the rest of the string */
    if ((str + index + 1)!=0)
        static_str = (str + index + 1);
    else
        static_str = 0;

        return str;
}

Ниже приведен пример использования...

  Example Usage
      char str[] = "A,B,,,C";
      printf("1 %s\n",zstring_strtok(s,","));
      printf("2 %s\n",zstring_strtok(NULL,","));
      printf("3 %s\n",zstring_strtok(NULL,","));
      printf("4 %s\n",zstring_strtok(NULL,","));
      printf("5 %s\n",zstring_strtok(NULL,","));
      printf("6 %s\n",zstring_strtok(NULL,","));

  Example Output
      1 A
      2 B
      3 ,
      4 ,
      5 C
      6 (null)

Библиотека может быть загружена из Github https://github.com/fnoyanisi/zString

Ответ 10

Не проверено, возможно, неправильно, но должно дать вам хорошее начало в том, как он должен работать:

*char[] str_split(char* str, char delim) {

    int begin = 0;
    int end = 0;
    int j = 0;
    int i = 0;
    char *buf[NUM];

    while (i < strlen(str)) {

        if(*str == delim) {

            buf[j] = malloc(sizeof(char) * (end-begin));
            strncpy(buf[j], *(str + begin), (end-begin));
            begin = end;
            j++;

        }

        end++;
        i++;

    }

    return buf;

}

Ответ 11

Попробуйте использовать это.

char** strsplit(char* str, const char* delim){
    char** res = NULL;
    char*  part;
    int i = 0;

    char* aux = strdup(str);

    part = strdup(strtok(aux, delim));

    while(part){
        res = (char**)realloc(res, (i + 1) * sizeof(char*));
        *(res + i) = strdup(part);

        part = strdup(strtok(NULL, delim));
        i++;
    }

    res = (char**)realloc(res, i * sizeof(char*));
    *(res + i) = NULL;

    return res;
}

Ответ 12

Это функция разделения строк, которая может обрабатывать многосимвольные разделители. Обратите внимание, что если разделитель длиннее разделяемой строки, то для buffer и stringLengths будет установлено значение (void *) 0, а для numStrings будет установлено значение 0.

Этот алгоритм был проверен и работает. (Отказ от ответственности: он не был протестирован для строк, не относящихся к ASCII, и предполагает, что вызывающая сторона задала допустимые параметры)

void splitString(const char *original, const char *delimiter, char ** * buffer, int * numStrings, int * * stringLengths){
    const int lo = strlen(original);
    const int ld = strlen(delimiter);
    if(ld > lo){
        *buffer = (void *)0;
        *numStrings = 0;
        *stringLengths = (void *)0;
        return;
    }

    *numStrings = 1;

    for(int i = 0;i < (lo - ld);i++){
        if(strncmp(&original[i], delimiter, ld) == 0) {
            i += (ld - 1);
            (*numStrings)++;
        }
    }

    *stringLengths = (int *) malloc(sizeof(int) * *numStrings);

    int currentStringLength = 0;
    int currentStringNumber = 0;
    int delimiterTokenDecrementCounter = 0;
    for(int i = 0;i < lo;i++){
        if(delimiterTokenDecrementCounter > 0){
            delimiterTokenDecrementCounter--;
        } else if(i < (lo - ld)){
            if(strncmp(&original[i], delimiter, ld) == 0){
                (*stringLengths)[currentStringNumber] = currentStringLength;
                currentStringNumber++;
                currentStringLength = 0;
                delimiterTokenDecrementCounter = ld - 1;
            } else {
                currentStringLength++;
            }
        } else {
            currentStringLength++;
        }

        if(i == (lo - 1)){
            (*stringLengths)[currentStringNumber] = currentStringLength;
        }
    }

    *buffer = (char **) malloc(sizeof(char *) * (*numStrings));
    for(int i = 0;i < *numStrings;i++){
        (*buffer)[i] = (char *) malloc(sizeof(char) * ((*stringLengths)[i] + 1));
    }

    currentStringNumber = 0;
    currentStringLength = 0;
    delimiterTokenDecrementCounter = 0;
    for(int i = 0;i < lo;i++){
        if(delimiterTokenDecrementCounter > 0){
            delimiterTokenDecrementCounter--;
        } else if(currentStringLength >= (*stringLengths)[currentStringNumber]){
            (*buffer)[currentStringNumber][currentStringLength] = 0;
            delimiterTokenDecrementCounter = ld - 1;
            currentStringLength = 0;
            currentStringNumber++;
        } else {
            (*buffer)[currentStringNumber][currentStringLength] = (char)original[i];
            currentStringLength++;
        }
    }
    buffer[currentStringNumber][currentStringLength] = 0;
}

Пример кода:

int main(){
    const char *string = "STRING-1 DELIM string-2 DELIM sTrInG-3";
    char **buffer;
    int numStrings;
    int * stringLengths;

    splitString(string, " DELIM ", &buffer, &numStrings, &stringLengths);

    for(int i = 0;i < numStrings;i++){
        printf("String: %s\n", buffer[i]);
    }
}

библиотеки:

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

Ответ 13

Этот оптимизированный метод создает (или обновляет существующий) массив указателей в * result и возвращает количество элементов в * count.

Используйте "max", чтобы указать максимальное количество ожидаемых строк (когда вы указываете существующий массив или любой другой reaseon), иначе установите его на 0

Чтобы сравнить с списком разделителей, определите delim как char * и замените строку:

if (str[i]==delim) {

с двумя следующими строками:

 char *c=delim; while(*c && *c!=str[i]) c++;
 if (*c) {

Enjoy

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

char **split(char *str, size_t len, char delim, char ***result, unsigned long *count, unsigned long max) {
  size_t i;
  char **_result;

  // there is at least one string returned
  *count=1;

  _result= *result;

  // when the result array is specified, fill it during the first pass
  if (_result) {
    _result[0]=str;
  }

  // scan the string for delimiter, up to specified length
  for (i=0; i<len; ++i) {

    // to compare against a list of delimiters,
    // define delim as a string and replace 
    // the next line:
    //     if (str[i]==delim) {
    //
    // with the two following lines:
    //     char *c=delim; while(*c && *c!=str[i]) c++;
    //     if (*c) {
    //       
    if (str[i]==delim) {

      // replace delimiter with zero
      str[i]=0;

      // when result array is specified, fill it during the first pass
      if (_result) {
        _result[*count]=str+i+1;
      }

      // increment count for each separator found
      ++(*count);

      // if max is specified, dont go further
      if (max && *count==max)  {
        break;
      }

    }
  }

  // when result array is specified, we are done here
  if (_result) {
    return _result;
  }

  // else allocate memory for result
  // and fill the result array                                                                                    

  *result=malloc((*count)*sizeof(char*));
  if (!*result) {
    return NULL;
  }
  _result=*result;

  // add first string to result
  _result[0]=str;

  // if theres more strings
  for (i=1; i<*count; ++i) {

    // find next string
    while(*str) ++str;
    ++str;

    // add next string to result
    _result[i]=str;

  }

  return _result;
}  

Пример использования:

#include <stdio.h>

int main(int argc, char **argv) {
  char *str="JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
  char **result=malloc(6*sizeof(char*));
  char **result2=0;
  unsigned long count;
  unsigned long count2;
  unsigned long i;

  split(strdup(str),strlen(str),',',&result,&count,6);
  split(strdup(str),strlen(str),',',&result2,&count2,0);

  if (result)
  for (i=0; i<count; ++i) {
    printf("%s\n",result[i]);
  }

  printf("\n");

  if (result2)
  for (i=0; i<count2; ++i) {
    printf("%s\n", result2[i]);
  }

  return 0;

}

Ответ 14

Я думаю, что следующее решение идеально:

  • Не уничтожает исходную строку
  • Re-entrant - то есть вы можете безопасно вызвать его из любого места в одном или нескольких потоках.
  • Портативный
  • Правильно обрабатывает несколько разделителей.
  • Быстрый и эффективный

Объяснение кода:

  • Определить структуру token для хранения адреса и длины токенов
  • Выделите достаточно памяти для них в худшем случае, а это когда str состоит исключительно из сепараторов, поэтому существует strlen(str) + 1 тоны, все пустые строки
  • Сканировать str, записывая адрес и длину каждого токена
  • Используйте это, чтобы выделить выходной массив правильного размера, включая дополнительное пространство для значения NULL сторожевого знака
  • Выделить, скопировать и добавить маркеры с использованием начала и длины информация - используйте memcpy, как это быстрее, чем strcpy, и мы знаем длины
  • Свободный адрес и длина массива маркеров
  • Возвращает массив токенов
typedef struct {
    const char *start;
    size_t len;
} token;

char **split(const char *str, char sep)
{
    char **array;
    unsigned int start = 0, stop, toks = 0, t;
    token *tokens = malloc((strlen(str) + 1) * sizeof(token));
    for (stop = 0; str[stop]; stop++) {
        if (str[stop] == sep) {
            tokens[toks].start = str + start;
            tokens[toks].len = stop - start;
            toks++;
            start = stop + 1;
        }
    }
    /* Mop up the last token */
    tokens[toks].start = str + start;
    tokens[toks].len = stop - start;
    toks++;
    array = malloc((toks + 1) * sizeof(char*));
    for (t = 0; t < toks; t++) {
        /* Calloc makes it nul-terminated */
        char *token = calloc(tokens[t].len + 1, 1);
        memcpy(token, tokens[t].start, tokens[t].len);
        array[t] = token;
    }
    /* Add a sentinel */
    array[t] = NULL; 
    free(tokens);
    return array;
}

Примечание malloc проверка опущена для краткости.

В общем, я бы не возвращал массив указателей char * из функции split, подобной этой, поскольку он налагает большую ответственность на вызывающего, чтобы освободить их правильно. Интерфейс, который я предпочитаю, должен позволить вызывающему абоненту передать функцию обратного вызова и называть это для каждого токена, как я описал здесь: Разделить строку в C.

Ответ 15

Моя версия:

int split(char* str, const char delimeter, char*** args) {
    int cnt = 1;
    char* t = str;

    while (*t == delimeter) t++;

    char* t2 = t;
    while (*(t2++))
        if (*t2 == delimeter && *(t2 + 1) != delimeter && *(t2 + 1) != 0) cnt++;

    (*args) = malloc(sizeof(char*) * cnt);

    for(int i = 0; i < cnt; i++) {
        char* ts = t;
        while (*t != delimeter && *t != 0) t++;

        int len = (t - ts + 1);
        (*args)[i] = malloc(sizeof(char) * len);
        memcpy((*args)[i], ts, sizeof(char) * (len - 1));
        (*args)[i][len - 1] = 0;

        while (*t == delimeter) t++;
    }

    return cnt;
}

Ответ 16

Мой подход заключается в том, чтобы отсканировать строку и дать указателям указать каждый символ после разделителей (и первого символа), в то же время назначить появления разделителя в строке на "\ 0".
Сначала сделайте копию исходной строки (так как она константа), а затем получите количество разделов путем сканирования, чтобы передать ее в параметр указателя len. После этого наведите указатель на первый указатель на указатель строки копирования, затем сканируйте строку-копию: однажды встретите разделитель, назначьте его "\ 0", поэтому предыдущая строка результата будет завершена, и укажите следующий указатель строки результата на следующий указатель символов.

char** split(char* a_str, const char a_delim, int* len){
    char* s = (char*)malloc(sizeof(char) * strlen(a_str));
    strcpy(s, a_str);
    char* tmp = a_str;
    int count = 0;
    while (*tmp != '\0'){
        if (*tmp == a_delim) count += 1;
        tmp += 1;
    }
    *len = count;
    char** results = (char**)malloc(count * sizeof(char*));
    results[0] = s;
    int i = 1;
    while (*s!='\0'){
        if (*s == a_delim){
            *s = '\0';
            s += 1;
            results[i++] = s;
        }
        else s += 1;
    }
    return results;
}

Ответ 17

Мой код (протестирован):

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

int dtmsplit(char *str, const char *delim, char ***array, int *length ) {
  int i=0;
  char *token;
  char **res = (char **) malloc(0 * sizeof(char *));

  /* get the first token */
   token = strtok(str, delim);
   while( token != NULL ) 
   {
        res = (char **) realloc(res, (i + 1) * sizeof(char *));
        res[i] = token;
        i++;
      token = strtok(NULL, delim);
   }
   *array = res;
   *length = i;
  return 1;
}

int main()
{
    int i;
    int c = 0;
    char **arr = NULL;

    int count =0;

    char str[80] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
    c = dtmsplit(str, ",", &arr, &count);
    printf("Found %d tokens.\n", count);

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

   return(0);
}

Результат:

Found 12 tokens.
string #0: JAN
string #1: FEB
string #2: MAR
string #3: APR
string #4: MAY
string #5: JUN
string #6: JUL
string #7: AUG
string #8: SEP
string #9: OCT
string #10: NOV
string #11: DEC

Ответ 18

Explode и implode - начальная строка остается неповрежденной, распределение динамической памяти

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

typedef struct
{
    uintptr_t   ptr;
    int         size;
} token_t;

int explode(char *str, int slen, const char *delimiter, token_t **tokens)
{
    int i = 0, c1 = 0, c2 = 0;

    for(i = 0; i <= slen; i++)
    {
            if(str[i] == *delimiter)
            {
                c1++;
            }
    }

    if(c1 == 0)
    {
            return -1;
    }

    *tokens = (token_t*)calloc((c1 + 1), sizeof(token_t));
    ((*tokens)[c2]).ptr = (uintptr_t)str;

    i = 0; 
    while(i <= slen)
    {
        if((str[i] == *delimiter) || (i == slen))
        {
                ((*tokens)[c2]).size = (int)((uintptr_t)&(str[i]) - (uintptr_t)(((*tokens)[c2]).ptr));
                if(i < slen)
                {
                    c2++;
                    ((*tokens)[c2]).ptr = (uintptr_t)&(str[i + 1]);
                }
        }
        i++;
    }
    return (c1 + 1);
}

char* implode(token_t *tokens, int size, const char *delimiter)
{
    int     i, len = 0;
    char    *str;

    for(i = 0; i < len; i++)
    {
        len += tokens[i].size + 1;
    }

    str = (char*)calloc(len, sizeof(char));

    len = 0;
    for(i = 0; i < size; i++)
    {
        memcpy((void*)&str[len], (void*)tokens[i].ptr, tokens[i].size);
        len += tokens[i].size;
        str[(len++)] = *delimiter;
    }

    str[len - 1] = '\0';

    return str;
}

Использование:

int main(int argc, char **argv)
{
    int         i, c;
    char        *exp = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
    token_t     *tokens;
    char        *imp;

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

    if((c = explode(exp, strlen(exp), ",", &tokens)) > 0)
    {
        imp = implode(tokens, c, ",");
        printf("%s\n", imp);

        for(i = 0; i < c; i++)
        {
            printf("%.*s, %d\n", tokens[i].size, (char*)tokens[i].ptr, tokens[i].size);
        }
    }

    free((void*)tokens);
    free((void*)imp);
    return 0;
}

Ответ 19

Если вы хотите использовать внешнюю библиотеку, я не могу рекомендовать bstrlib. Это требует немного дополнительной настройки, но более простое использование в конечном итоге.

Например, разделите строку ниже, сначала создайте bstring с вызовом bfromcstr(). (A bstring является оберткой вокруг буфера char). Затем разделите строку на запятую, сохраняя результат в struct bstrList, который имеет поля qty и массив entry, который представляет собой массив из bstring s.

bstrlib имеет много других функций для работы на bstring s

Просто как пирог...

#include "bstrlib.h"
#include <stdio.h>
int main() {
  int i;
  char *tmp = "Hello,World,sak";
  bstring bstr = bfromcstr(tmp);
  struct bstrList *blist = bsplit(bstr, ',');
  printf("num %d\n", blist->qty);
  for(i=0;i<blist->qty;i++) {
    printf("%d: %s\n", i, bstr2cstr(blist->entry[i], '_'));
  }

}

Ответ 20

Есть несколько проблем с strtok(), перечисленными здесь: http://benpfaff.org/writings/clc/strtok.html

Следовательно, лучше избегать strtok.

Теперь рассмотрим строку, содержащую пустое поле, следующим образом:

char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here

Вы можете использовать простую функцию, чтобы иметь возможность конвертировать String в формате CSV, чтобы прочитать их в массиве float:

int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim);

Мы указали разделитель здесь как запятую. Он работает с другим разделителем символов.

Найдите Использование ниже:

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



int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim);

  void main()
 {

    char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here
    float floatArr[10]; // specify size of float array here 
    int totalValues = 0;
    char myDelim = ','; // specify delimiter here 

    printf("myCSVString == %s \n",&myCSVString[0]);

    totalValues = strCSV2Float(&floatArr[0] , &myCSVString[0], myDelim); // call the function here 

    int floatValueCount = 0;

    for (floatValueCount = 0 ; floatValueCount < totalValues ; floatValueCount++)
    {

      printf("floatArr[%d] = %f\n",floatValueCount , floatArr[floatValueCount]);

    }

 }




int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim)
{

int strLen = 0;
int commaCount =0; // count the number of commas
int commaCountOld =0; // count the number of commas
int wordEndChar = 0;
int wordStartChar = -1;
int wordLength =0;


   for(strLen=0; myCSVStringing[strLen] != '\0'; strLen++) // first get the string length
   {

       if ( (myCSVStringing[strLen] == delim)  || ( myCSVStringing[strLen+1] == '\0' ))
        {
           commaCount++;
           wordEndChar = strLen;
        }
       if ( (commaCount - commaCountOld) > 0 )
        {
          int aIter =0;
          wordLength = (wordEndChar - wordStartChar);
          char word[55] = "";
          for (aIter = 0;  aIter < wordLength; aIter++)
          {
            word[aIter] = myCSVStringing[strLen-wordLength+aIter+1];
          }

          if (word[aIter-1] == delim) 
           word[aIter-1] = '\0';

          //  printf("\n");
          word[wordLength] = '\0';
          strFloatArray[commaCount-1] = atof(&word[0]);

          wordLength = 0;
          wordStartChar = wordEndChar;
          commaCountOld = commaCount;

        }  
  }

  return commaCount;

}

Выход выглядит следующим образом:

myCSVString == -1.4,2.6,,-0.24,1.26 
floatArr[0] = -1.400000
floatArr[1] = 2.600000
floatArr[2] = 0.000000
floatArr[3] = -0.240000
floatArr[4] = 1.260000

Ответ 21

Для: Хасан А. Эль-Сеуди

Ваш билет закрыт, поэтому я не могу ответить на него ^^ '. Но вы можете попробовать следующее:

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

int     countChar(char *str)
{
  int   count;
  int   i;

  i = 0;
  count = 0;
  while (str[i] != '=') // our delimiter character
    {
      i++;
      count++;
    }

  return (count);
}

void    split(char *str)
{
  int   i;
  int   j;
  int   count;
  int   restCount;
  char  *str1;
  char  *str2;

  i = 0;
  j = 0;
  count = countChar(str) - 1; // we have our str1 lenght, -1 for the ' '
  restCount = (strlen(str) - count) -1; // we have our str2 legnht, -1 for the ' '
  str1 = malloc(sizeof(char) * count);
  str2 = malloc(sizeof(char) * restCount);

  while(i < count)
    {
      str1[i] = str[i++];
    }
  i = i + 2; // to jump directly to the first char of our str2 (no ' = ')
  while (str[i])
    {
      str2[j++] = str[i++];
    }
  printf("str1 = %s, str2 = %s\n", str1, str2);
}

int     main()
{
  char  *str = "Xo = 100k";

  split(str);

  return (0);
}'

Ответ 22

Еще один ответ (здесь был перемещен из здесь):

Попробуйте использовать функцию strtok:

см. подробности на эту тему здесь или здесь

Проблема здесь в том, что вам нужно немедленно обработать words. Если вы хотите сохранить его в массиве, вы должны выделить correct size, потому что он неизвестен.

Итак, например:

char **Split(char *in_text, char *in_sep)
{
    char **ret = NULL;
    int count = 0;
    char *tmp = strdup(in_text);
    char *pos = tmp;

    // This is the pass ONE: we count 
    while ((pos = strtok(pos, in_sep)) != NULL)
    {
        count++;
        pos = NULL;
    }

    // NOTE: the function strtok changes the content of the string! So we free and duplicate it again! 
    free(tmp);
    pos = tmp = strdup(in_text);

    // We create a NULL terminated array hence the +1
    ret = calloc(count+1, sizeof(char*));
    // TODO: You have to test the `ret` for NULL here

    // This is the pass TWO: we store
    count = 0;
    while ((pos = strtok(pos, in_sep)) != NULL)
    {
        ret[count] = strdup(pos);
        count++;
        pos = NULL;
    }
    free(tmp);

    return count;
}

// Use this to free
void Free_Array(char** in_array)
{
    char *pos = in_array;

    while (pos[0] != NULL)
    {
        free(pos[0]);
        pos++;

    }

    free(in_array);

}

Примечание: Мы используем один и тот же цикл и функцию для вычисления счетчиков (передать один) и для создания копий (передать два), чтобы избежать проблем с распределением.

Примечание 2: Вы можете использовать некоторую другую реализацию strtok причин, упомянутых в отдельных сообщениях.

Вы можете использовать это как:

int main(void)
{
  char **array = Split("Hello World!", " ");
  // Now you have the array
  // ...

  // Then free the memory
  Free_Array(array);
  array = NULL;
  return 0;
}

(я не тестировал его, поэтому, пожалуйста, дайте мне знать, если это не сработает!)

Ответ 23

static int count_token(char *iptr, char delim) {

        int token_count = 0;
        while (*iptr && isspace(*iptr))
            iptr++;
        while (*iptr) {
            if ((*iptr != delim)) {
                token_count++;
                while (*iptr && (*iptr != delim))
                    iptr++;
            }
            else {
                iptr++;
            }
        }
        return token_count;
    }

    static char** split(char* input, int* argc){
        char**  argv;
        int token_count = count_token(input, ' ');
        argv = (char**)malloc(sizeof(char*)*token_count);

        int i = 0;
        char *token = strtok(input, " ");
        while(token) {
            puts(token);
            argv[i] = strdup(token);
            token = strtok(NULL, " ");
            i++;
        }
        assert(i == token_count);
        *argc = token_count;
        return argv;
    }

Ответ 24

Это может решить вашу цель

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

int main()
{
    int i = 0,j = 0,k = 0;
    char name[] = "jrSmith-Rock";
    int length = strlen(name);
    char store[100][100];
    for(i = 0, j = 0,k = 0; i < length;) {
        if((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z')) {
            store[j][k] = name[i];
            k++;
            i++;
        }
        else{
            while(! isalpha(name[i])) {
                i++;
            }
            j++;
            k = 0;
        }
    }

    for(i = 0; i <= j; i++) {
        printf("%s\n", store[i]);
    }
    return 0;
}

Выход:

jrSmith
Rock

Ответ 25

Это другой подход, работающий и для больших файлов.

/******************************************************************************

                            Online C Compiler.
                Code, Compile, Run and Debug C program online.
Write your code in this editor and press "Run" button to compile and execute it.

*******************************************************************************/

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

char* aux_slice(const char* str, char delimiter, const char** pRet);
char **split(const char *str, char delimiter, int *ret_size);



int main()
{
    int count = 0, i = 0;
    char** splits = split(",,, 1,2,3,4,5,6,7,8,9,10, aaaa, a,a a,aa,,a,,,,a,a a,a a,a,,,,,ashajhsas asjas,,a,a,,aa"
    "aaaaaaaaaaa.......,p,p,p,p,p,p,p, this is last,,,,,,,,,,", ',', &count);
    printf("Strings (%d)\n", count); 
    for (i=0 ; i < count; i++) {
        printf("%s\n", splits[i]);
    }
    for(i=0; i < count; i++) {
        free(splits[i]);
    }
    free(splits); 
    return 0;
}




char* aux_slice(const char* str, char delimiter, const char** pRet)
{
    int size = 0, i = 0;
    const char* begin = str;
    char  *ret = NULL;
    int match = 0;

    if (!str) {
        return NULL;
    }

    while (*begin != '\0') {
        if (*begin == delimiter) {
            match++;
            break;
        }
        size++;
        begin++;
    }

    ret = (char*)malloc(sizeof(char) * size);

    if(ret == NULL) {
        return NULL;
    }

    if (match) {
        /* we have a delimiter ??? */
        for(i = 0; str[i] != delimiter; ++i) {
            ret[i] = str[i];
        }

        ret[i] = '\0';

        while (*begin == delimiter) {
            begin++;
        }
        (*pRet) = begin;

    } else {
        /* or we just copy the remaining string.... */
        for(i=0; str[i] != '\0'; ++i) {
            ret[i] = str[i];
        }
        ret[i] = '\0';
        (*pRet) = NULL;
    }

    return ret;
}


char **split(const char *str, char delimiter, int *ret_size)
{
    int diff = 0, splits = 0, i=0;
    const char* begin = str;
    const char* end = &str[strlen(str)-1];
    while (*begin == delimiter) begin++;
    while (*end == delimiter) end--;
    diff = (end - begin)+1;

    while (i < diff) {
        // avoid cases of adjacent delimiters
        // like "str1,str2,,,,,str3
        if (begin[i] == delimiter) {
            while (begin[i] == delimiter) i++;
            splits++;
        }
        i++;
    }
    splits += 1;
    *ret_size = splits;
    char** split_str = (char**)malloc(sizeof(char**)*splits);

    if (split_str == NULL) {
        return NULL;
    }
    for(i=0; i < splits; ++i) {
        split_str[i] = aux_slice(begin, delimiter, &begin);
    }
    return split_str;
}

Демо: https://onlinegdb.com/BJlWVdzGf