Chia chuỗi với dấu phân cách trong C


155

Làm thế nào để tôi viết một hàm để phân tách và trả về một mảng cho một chuỗi với các dấu phân cách trong ngôn ngữ lập trình C?

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

25
Bạn có thể sử dụng strtokchức năng từ thư viện tiêu chuẩn để đạt được điều tương tự.
Daniel Kamil Kozar


Một nhận xét ... điểm mấu chốt cho strtok()chức năng gia đình là sự hiểu biết về static variablesC. tức là cách họ hành xử giữa các cuộc gọi chức năng kế tiếp mà họ được sử dụng. Xem mã của tôi dưới đây
fnisi

Câu trả lời:


165

Bạn có thể sử dụng strtok()hàm để phân tách một chuỗi (và chỉ định dấu phân cách sẽ sử dụng). Lưu ý rằng strtok()sẽ sửa đổi chuỗi được truyền vào nó. Nếu chuỗi gốc được yêu cầu ở nơi khác, hãy tạo một bản sao của chuỗi đó và chuyển bản sao đó sang strtok().

BIÊN TẬP:

Ví dụ (lưu ý rằng nó không xử lý các dấu phân cách liên tiếp, ví dụ: "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;
}

Đầu ra:

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

60
Chào! những strtokđược đánh dấu là lỗi thời bởi strsep(3)trong trang người đàn ông.
osgx

4
Vì đây có thể là câu hỏi / câu trả lời chính tắc trên Stack Overflow cho điều này, không có một số cảnh báo liên quan đến đa luồng bằng strtok?
Peter Mortensen

3
@osgx Theo trang đó, strseplà một thay thế cho strtok, nhưng strtokđược ưa thích cho tính di động. Vì vậy, trừ khi bạn cần hỗ trợ cho các trường trống hoặc tách nhiều chuỗi cùng một lúc, strtoklà lựa chọn tốt hơn.

4
@Dojo: Nó nhớ nó; đó là một trong những lý do nó có vấn đề. Nó sẽ tốt hơn để sử dụng strtok_s()(Microsoft, C11 Phụ lục K, tùy chọn) hoặc strtok_r()(POSIX) hơn là đơn giản strtok(). Plain strtok()là ác trong một chức năng thư viện. Không có chức năng gọi chức năng thư viện có thể được sử dụng strtok()tại thời điểm đó và không có chức năng nào được gọi bởi chức năng thư viện có thể gọi strtok().
Jonathan Leffler

3
Chỉ là một lưu ý strtok()không phải là luồng an toàn (vì lý do @JonathanLeffler đã đề cập) và do đó toàn bộ chức năng này không phải là luồng an toàn. Nếu bạn cố gắng sử dụng điều này trong một môi trường có bước đi, bạn sẽ nhận được kết quả thất thường và không thể đoán trước. Thay thế strtok()để strtok_r()khắc phục vấn đề này.
Sean W

70

Tôi nghĩ strsepvẫn là công cụ tốt nhất cho việc này:

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

Đó đúng là một dòng chia tách một chuỗi.

Các dấu ngoặc đơn phụ là một yếu tố phong cách để chỉ ra rằng chúng tôi cố tình kiểm tra kết quả của một bài tập, không phải là toán tử đẳng thức ==.

Để mô hình đó hoạt động, tokenstrcả hai đều có loại char *. Nếu bạn đã bắt đầu với một chuỗi ký tự, thì trước tiên bạn muốn tạo một bản sao của chuỗi đó:

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

Nếu hai dấu phân cách xuất hiện cùng nhau str, bạn sẽ nhận được một tokengiá trị là chuỗi rỗng. Giá trị củastr được sửa đổi trong đó mỗi dấu phân cách gặp phải được ghi đè bằng byte bằng 0 - một lý do chính đáng khác để sao chép chuỗi được phân tích cú pháp trước.

Trong một bình luận, có người cho rằng điều đó strtoktốt hơn là strsepstrtokdễ mang theo hơn. Ubuntu và Mac OS X có strsep; Thật an toàn khi đoán rằng các hệ thống unixy khác cũng làm như vậy. Windows thiếu strsep, nhưng nó strbrkcho phép strsepthay thế ngắn và ngọt ngào này :

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

Dưới đây là một lời giải thích tốt strsepvs strtok. Những ưu và nhược điểm có thể được đánh giá một cách chủ quan; tuy nhiên, tôi nghĩ đó là một dấu hiệu nhận biết strsepđược thiết kế để thay thế strtok.


3
Chính xác hơn về tính di động: nó không phảiPOSIX 7 , mà là BSD có nguồn gốc và được triển khai trên glibc .
Ciro Santilli 郝海东 冠状 病 事件

Tôi vừa định hỏi ... Pelle C có strdup (), nhưng không có strsep ().
ndtsc

1
Tại sao lại tofreelà miễn phí str?
Sdlion

1
Bạn không thể miễn phí strvì giá trị của nó có thể được thay đổi bằng các cuộc gọi đến strsep(). Giá trị của các tofreeđiểm nhất quán bắt đầu từ bộ nhớ bạn muốn giải phóng.
Tyler

26

Chuỗi mã thông báo mã này sẽ đưa bạn đi đúng hướng.

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

13

Phương pháp dưới đây sẽ thực hiện tất cả các công việc (cấp phát bộ nhớ, đếm chiều dài) cho bạn. Có thể tìm thêm thông tin và mô tả tại đây - Triển khai phương thức Java String.split () để phân tách chuỗi 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;
}

Làm thế nào để sử dụng nó:

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

4
Lập trình viên ba sao Huh :)) Điều này nghe có vẻ thú vị.
Michi

Khi tôi làm điều này, nó sẽ thêm quá nhiều vào mã thông báo cuối cùng hoặc phân bổ quá nhiều bộ nhớ. Đây là đầu ra: found 10 tokens. string #0: Hello, string #1: this string #2: is string #3: a string #4: test string #5: module string #6: for string #7: the string #8: string string #9: splitting.¢
KeizerHarm

2
Ví dụ này có nhiều rò rỉ bộ nhớ. Đối với bất cứ ai đọc điều này, không sử dụng phương pháp này. Thay vào đó, hãy ưu tiên các phương pháp tiếp cận mã thông báo strtok hoặc strsep.
Jorma Rebane

7

Đây là hai xu của tôi:

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

Sử dụng:

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

3
oh boi, ba con trỏ! Tôi đã sợ sử dụng nó lol chỉ là tôi, tôi không tốt lắm với con trỏ trong c.
Hafiz Temuri

Cảm ơn người đàn ông, tất cả các câu trả lời strtok ở trên đã không hoạt động trong trường hợp của tôi ngay cả sau rất nhiều nỗ lực, và mã của bạn hoạt động như một cơ duyên!
hmmftg

4

Trong ví dụ trên, sẽ có một cách để trả về một chuỗi các chuỗi kết thúc null (như bạn muốn) thay thế trong chuỗi. Mặc dù vậy, nó sẽ không thể vượt qua một chuỗi ký tự, vì nó sẽ phải được sửa đổi bởi hàm:

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

Có lẽ có một cách gọn gàng hơn để làm điều đó, nhưng bạn có ý tưởng.


3

Hàm này lấy một chuỗi char * và phân tách nó bằng dấu phân cách. Có thể có nhiều dấu phân cách liên tiếp. Lưu ý rằng hàm sửa đổi chuỗi gốc. Bạn phải tạo một bản sao của chuỗi gốc trước nếu bạn cần bản gốc để không bị thay đổi. Hàm này không sử dụng bất kỳ lệnh gọi hàm chuỗi nào vì vậy nó có thể nhanh hơn một chút so với các hàm khác. Nếu bạn không quan tâm đến việc cấp phát bộ nhớ, bạn có thể phân bổ sub_strings ở đầu hàm với kích thước strlen (src_str) / 2 và (như "phiên bản" c ++ đã đề cập) bỏ qua nửa dưới của hàm. Nếu bạn làm điều này, chức năng sẽ bị giảm xuống O (N), nhưng cách tối ưu hóa bộ nhớ được hiển thị bên dưới là O (2N).

Chức năng:

char** str_split(char *src_str, const char deliminator, size_t &num_sub_str){
  //replace deliminator's 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);
}

Làm thế nào để sử dụng nó:

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

3
#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's an allocation problem but I did the triple array here.  Note that
 *  upon the hitting two delim's 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's 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;
}

3

Dưới đây là strtok()triển khai của tôi từ thư viện zString . zstring_strtok()khác với thư viện tiêu chuẩnstrtok() theo cách nó xử lý các dấu phân cách liên tiếp.

Chỉ cần xem mã dưới đây, chắc chắn rằng bạn sẽ có ý tưởng về cách thức hoạt động của nó (Tôi đã cố gắng sử dụng càng nhiều bình luận càng tốt)

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

Dưới đây là một ví dụ sử dụng ...

  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)

Có thể tải xuống thư viện từ Github https://github.com/fnoyanisi/zString


tốt đẹp đó là những gì tôi đang tìm kiếm.
Kostia Kim

3

Tôi nghĩ giải pháp sau đây là lý tưởng:

  • Không phá hủy chuỗi nguồn
  • Đăng ký lại - tức là bạn có thể gọi nó một cách an toàn từ bất cứ đâu trong một hoặc nhiều chủ đề
  • Di động
  • Xử lý nhiều dấu phân cách chính xác
  • Nhanh chóng và hiệu quả

Giải thích về mã:

  1. Xác định cấu trúc tokenđể lưu trữ địa chỉ và độ dài của mã thông báo
  2. Phân bổ đủ bộ nhớ cho những thứ này trong trường hợp xấu nhất, đó là khi strđược tạo thành hoàn toàn bằng các dải phân cách để có strlen(str) + 1 mã thông báo, tất cả chúng đều là chuỗi rỗng
  3. Quét strghi lại địa chỉ và độ dài của mỗi mã thông báo
  4. Sử dụng công cụ này để phân bổ mảng đầu ra có kích thước chính xác, bao gồm cả không gian thừa cho NULLgiá trị sentinel
  5. Phân bổ, sao chép và thêm mã thông báo bằng cách sử dụng thông tin bắt đầu và độ dài - sử dụng memcpyvì nó nhanh hơnstrcpy và chúng tôi biết độ dài
  6. Giải phóng địa chỉ mã thông báo và mảng chiều dài
  7. Trả về mảng mã thông báo
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;
}

Lưu ý malloc kiểm tra bỏ qua cho ngắn gọn.

Nói chung, tôi sẽ không trả về một loạt các char *con trỏ từ một hàm phân tách như thế này vì nó đặt rất nhiều trách nhiệm cho người gọi để giải phóng chúng một cách chính xác. Một giao diện tôi thích là để cho phép người gọi để vượt qua một chức năng gọi lại và gọi đây là cho mỗi thẻ, như tôi đã mô tả ở đây: Chia một String trong C .


Quét hai lần phân tách có lẽ được khuyến khích hơn là phân bổ một mảng có khả năng lớn token.
chqrlie

2

Hãy thử sử dụng cái này.

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

2

Phương thức tối ưu hóa này tạo (hoặc cập nhật một mảng các con trỏ hiện có trong kết quả * và trả về số lượng phần tử trong * đếm.

Sử dụng "max" để chỉ ra số chuỗi tối đa bạn mong đợi (khi bạn chỉ định một mảng hiện có hoặc bất kỳ reaseon nào khác), khác đặt nó thành 0

Để so sánh với danh sách các dấu phân cách, xác định dấu phân cách là char * và thay thế dòng:

if (str[i]==delim) {

với hai dòng sau:

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

Thưởng thức

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

Ví dụ sử dụng:

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

}

2

Phiên bản của tôi:

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

2

Đây là một chức năng phân tách chuỗi có thể xử lý các dấu phân cách nhiều ký tự. Lưu ý rằng nếu dấu phân cách dài hơn chuỗi đang được phân tách, thì bufferstringLengthssẽ được đặt thành (void *) 0numStringssẽ được đặt thành 0.

Thuật toán này đã được thử nghiệm, và hoạt động. (Tuyên bố miễn trừ trách nhiệm: Chưa được kiểm tra các chuỗi không phải ASCII và giả định rằng người gọi đã cung cấp các tham số hợp lệ)

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

Mã mẫu:

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

Thư viện:

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

Làm thế nào để tôi gọi điều này từ chính? Tôi không biết những gì để vượt qua để đệm.
Aymon Fournier

Phân bổ logic là sai. realloc () trả về con trỏ mới và bạn loại bỏ giá trị trả về. Không có cách thích hợp để trả về con trỏ bộ nhớ mới - nguyên mẫu hàm nên được thay đổi để chấp nhận kích thước phân bổ buffervà để lại phân bổ cho người gọi, xử lý các phần tử kích thước tối đa.
Alex

@Alex Đã sửa, viết lại hoàn toàn và kiểm tra. Lưu ý: không chắc chắn liệu điều này có hoạt động cho phi ASCII hay không.
Élektra

Đối với người mới bắt đầu, đây không phải là mã C. Và tại sao bạn lại vượt qua con trỏ bằng tham chiếu thực tế trong C ++?
Kamiccolo

@Kamiccolo Tôi xin lỗi, làm thế nào chính xác đây không phải là mã C? Ngoài ra, tại sao việc truyền con trỏ bằng cách tham khảo một vấn đề ở đây?
Élektra

1

Cách tiếp cận của tôi là quét chuỗi và để con trỏ trỏ đến mọi ký tự sau dấu phân cách (và ký tự đầu tiên), đồng thời gán sự xuất hiện của dấu phân cách trong chuỗi thành '\ 0'.
Đầu tiên tạo một bản sao của chuỗi gốc (vì nó không đổi), sau đó lấy số lượng phân tách bằng cách quét nó chuyển nó đến tham số con trỏ len . Sau đó, trỏ con trỏ kết quả đầu tiên vào con trỏ chuỗi sao chép, sau đó quét chuỗi sao chép: một lần gặp một dấu phân cách, gán nó cho '\ 0', do đó chuỗi kết quả trước đó bị chấm dứt và trỏ con trỏ chuỗi kết quả tiếp theo sang tiếp theo con trỏ ký tự.

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

Phương pháp này sai. Tôi vừa bị xóa bài đăng này, nhưng sau đó tôi nhận ra nó có thể thú vị đối với một số bạn.
kim loại

1

Mã của tôi (đã kiểm tra):

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

Kết quả:

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

1
Xin lưu ý rằng hàm strtok thay đổi chuỗi 'str' đã được áp dụng cho!
SchLx

1

Explode & implode - chuỗi ban đầu vẫn còn nguyên, cấp phát bộ nhớ động

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

Sử dụng:

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

0

Nếu bạn sẵn sàng sử dụng một thư viện bên ngoài, tôi không thể khuyên bạn nên bstrlib đủ. Nó cần thêm một chút thiết lập, nhưng sẽ dễ sử dụng hơn trong thời gian dài.

Ví dụ: tách chuỗi bên dưới, đầu tiên tạo một chuỗi bstringvới bfromcstr()cuộc gọi. (A bstringlà một trình bao bọc xung quanh bộ đệm char). Tiếp theo, phân tách chuỗi trên dấu phẩy, lưu kết quả trong một struct bstrList, có các trường qtyvà một mảng entry, là một mảng của bstrings.

bstrlibcó nhiều chức năng khác để hoạt động trên bstrings

Dễ như ăn bánh...

#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], '_'));
  }

}

0

Một câu trả lời khác (điều này đã được chuyển đến đây từ đây ):

Hãy thử sử dụng hàm strtok:

xem chi tiết về chủ đề này ở đây hoặc ở đây

Vấn đề ở đây là bạn phải xử lý wordsngay lập tức. Nếu bạn muốn lưu trữ nó trong một mảng, bạn phải phân bổ correct sizecho phù thủy không rõ.

Ví dụ:

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

}

Lưu ý : Chúng tôi sử dụng cùng một vòng lặp và chức năng để tính các số đếm (vượt qua một) và để tạo các bản sao (vượt qua hai), để tránh các vấn đề phân bổ.

Lưu ý 2 : Bạn có thể sử dụng một số triển khai khác của strtok những lý do được đề cập trong các bài đăng riêng biệt.

Bạn có thể sử dụng như thế này:

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

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

(Tôi đã không kiểm tra nó, vì vậy xin vui lòng cho tôi biết nếu nó không hoạt động!)


0

Hai vấn đề xung quanh câu hỏi này là quản lý bộ nhớ và an toàn luồng. Như bạn có thể thấy từ rất nhiều bài đăng, đây không phải là một nhiệm vụ dễ dàng để thực hiện hoàn hảo trong C. Tôi muốn một giải pháp đó là:

  • Chủ đề an toàn. (strtok không phải là chủ đề an toàn)
  • Không sử dụng malloc hoặc bất kỳ dẫn xuất nào của nó (để tránh các vấn đề về quản lý bộ nhớ)
  • Kiểm tra giới hạn mảng trên các trường riêng lẻ (để tránh lỗi phân đoạn trên dữ liệu không xác định)
  • Hoạt động với các dấu tách trường nhiều byte (utf-8)
  • bỏ qua các trường bổ sung trong đầu vào
  • cung cấp thói quen lỗi mềm cho độ dài trường không hợp lệ

Giải pháp tôi đưa ra đáp ứng tất cả các tiêu chí này. Có thể cần thêm một chút công việc để thiết lập so với một số giải pháp khác được đăng ở đây, nhưng tôi nghĩ rằng trong thực tế, công việc bổ sung đáng giá để tránh những cạm bẫy chung của các giải pháp khác.

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

struct splitFieldType {
    char *field;
    int   maxLength;
};

typedef struct splitFieldType splitField;

int strsplit(splitField *fields, int expected, const char *input, const char *fieldSeparator, void (*softError)(int fieldNumber,int expected,int actual))  {
    int i;
    int fieldSeparatorLen=strlen(fieldSeparator);
    const char *tNext, *tLast=input;

    for (i=0; i<expected && (tNext=strstr(tLast, fieldSeparator))!=NULL; ++i) {
        int len=tNext-tLast;
        if (len>=fields[i].maxLength) {
            softError(i,fields[i].maxLength-1,len);
            len=fields[i].maxLength-1;
        }
        fields[i].field[len]=0;
        strncpy(fields[i].field,tLast,len);
        tLast=tNext+fieldSeparatorLen;
    }
    if (i<expected) {
        if (strlen(tLast)>fields[i].maxLength) {
            softError(i,fields[i].maxLength,strlen(tLast));
        } else {
            strcpy(fields[i].field,tLast);
        }
        return i+1;
    } else {
        return i;
    }
}


void monthSplitSoftError(int fieldNumber, int expected, int actual) {
    fprintf(stderr,"monthSplit: input field #%d is %d bytes, expected %d bytes\n",fieldNumber+1,actual,expected);
}


int main() {
  const char *fieldSeparator=",";
  const char *input="JAN,FEB,MAR,APRI,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,FOO,BAR";

  struct monthFieldsType {
    char field1[4];
    char field2[4];
    char field3[4];
    char field4[4];
    char field5[4];
    char field6[4];
    char field7[4];
    char field8[4];
    char field9[4];
    char field10[4];
    char field11[4];
    char field12[4];
  } monthFields;

  splitField inputFields[12] = {
    {monthFields.field1,  sizeof(monthFields.field1)},
    {monthFields.field2,  sizeof(monthFields.field2)},
    {monthFields.field3,  sizeof(monthFields.field3)},
    {monthFields.field4,  sizeof(monthFields.field4)},
    {monthFields.field5,  sizeof(monthFields.field5)},
    {monthFields.field6,  sizeof(monthFields.field6)},
    {monthFields.field7,  sizeof(monthFields.field7)},
    {monthFields.field8,  sizeof(monthFields.field8)},
    {monthFields.field9,  sizeof(monthFields.field9)},
    {monthFields.field10, sizeof(monthFields.field10)},
    {monthFields.field11, sizeof(monthFields.field11)},
    {monthFields.field12, sizeof(monthFields.field12)}
  };

  int expected=sizeof(inputFields)/sizeof(splitField);

  printf("input data: %s\n", input);
  printf("expecting %d fields\n",expected);

  int ct=strsplit(inputFields, expected, input, fieldSeparator, monthSplitSoftError);

  if (ct!=expected) {
    printf("string split %d fields, expected %d\n", ct,expected);
  }

  for (int i=0;i<expected;++i) {
    printf("field %d: %s\n",i+1,inputFields[i].field);
  }

  printf("\n");
  printf("Direct structure access, field 10: %s", monthFields.field10);
}

Dưới đây là một ví dụ biên dịch và đầu ra. Lưu ý rằng trong ví dụ của tôi, tôi cố tình đánh vần "APRIL" để bạn có thể thấy lỗi mềm hoạt động như thế nào.

$ gcc strsplitExample.c && ./a.out
input data: JAN,FEB,MAR,APRIL,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,FOO,BAR
expecting 12 fields
monthSplit: input field #4 is 5 bytes, expected 3 bytes
field 1: JAN
field 2: FEB
field 3: MAR
field 4: APR
field 5: MAY
field 6: JUN
field 7: JUL
field 8: AUG
field 9: SEP
field 10: OCT
field 11: NOV
field 12: DEC

Direct structure access, field 10: OCT

Thưởng thức!


0

Dưới đây là một triển khai khác sẽ hoạt động an toàn để token hóa một chuỗi ký tự khớp với nguyên mẫu được yêu cầu trong câu hỏi trả về một con trỏ được phân bổ thành con trỏ cho char (ví dụ char **). Chuỗi phân cách có thể chứa nhiều ký tự và chuỗi đầu vào có thể chứa bất kỳ số lượng mã thông báo nào. Tất cả các phân bổ và phân bổ lại được xử lý bởi mallochoặc reallockhông có POSIX strdup.

Số lượng con trỏ ban đầu được phân bổ được kiểm soát bởi NPTRShằng số và giới hạn duy nhất là nó lớn hơn 0. Trả char **về chứa một sentinel NULL sau mã thông báo cuối cùng tương tự *argv[]và ở dạng có thể sử dụng bởi execv, execvpexecve.

Như với strtok()nhiều dấu phân cách tuần tự được coi là một dấu phân cách đơn, do đó "JAN,FEB,MAR,APR,MAY,,,JUN,JUL,AUG,SEP,OCT,NOV,DEC"sẽ được phân tích cú pháp như thể chỉ một ','phân tách duy nhất "MAY,JUN".

Các chức năng dưới đây được nhận xét nội tuyến và một đoạn ngắn main()đã được thêm vào tách các tháng. Số lượng con trỏ ban đầu được phân bổ đã được đặt tại 2để buộc ba lần tái phân bổ trong khi mã hóa chuỗi đầu vào:

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

#define NPTRS 2     /* initial number of pointers to allocate (must be > 0) */

/* split src into tokens with sentinel NULL after last token.
 * return allocated pointer-to-pointer with sentinel NULL on success,
 * or NULL on failure to allocate initial block of pointers. The number
 * of allocated pointers are doubled each time reallocation required.
 */
char **strsplit (const char *src, const char *delim)
{
    int i = 0, in = 0, nptrs = NPTRS;       /* index, in/out flag, ptr count */
    char **dest = NULL;                     /* ptr-to-ptr to allocate/fill */
    const char *p = src, *ep = p;           /* pointer and end-pointer */

    /* allocate/validate nptrs pointers for dest */
    if (!(dest = malloc (nptrs * sizeof *dest))) {
        perror ("malloc-dest");
        return NULL;
    }
    *dest = NULL;   /* set first pointer as sentinel NULL */

    for (;;) {  /* loop continually until end of src reached */
        if (!*ep || strchr (delim, *ep)) {  /* if at nul-char or delimiter char */
            size_t len = ep - p;            /* get length of token */
            if (in && len) {                /* in-word and chars in token */
                if (i == nptrs - 1) {       /* used pointer == allocated - 1? */
                    /* realloc dest to temporary pointer/validate */
                    void *tmp = realloc (dest, 2 * nptrs * sizeof *dest);
                    if (!tmp) {
                        perror ("realloc-dest");
                        break;  /* don't exit, original dest still valid */
                    }
                    dest = tmp;             /* assign reallocated block to dest */
                    nptrs *= 2;             /* increment allocated pointer count */
                }
                /* allocate/validate storage for token */
                if (!(dest[i] = malloc (len + 1))) {
                    perror ("malloc-dest[i]");
                    break;
                }
                memcpy (dest[i], p, len);   /* copy len chars to storage */
                dest[i++][len] = 0;         /* nul-terminate, advance index */
                dest[i] = NULL;             /* set next pointer NULL */
            }
            if (!*ep)                       /* if at end, break */
                break;
            in = 0;                         /* set in-word flag 0 (false) */
        }
        else {  /* normal word char */
            if (!in)                        /* if not in-word */
                p = ep;                     /* update start to end-pointer */
            in = 1;                         /* set in-word flag 1 (true) */
        }
        ep++;   /* advance to next character */
    }

    return dest;
}

int main (void) {

    char *str = "JAN,FEB,MAR,APR,MAY,,,JUN,JUL,AUG,SEP,OCT,NOV,DEC",
        **tokens;                           /* pointer to pointer to char */

    if ((tokens = strsplit (str, ","))) {   /* split string into tokens */
        for (char **p = tokens; *p; p++) {  /* loop over filled pointers */
            puts (*p);
            free (*p);      /* don't forget to free allocated strings */
        }
        free (tokens);      /* and pointers */
    }
}

Ví dụ sử dụng / đầu ra

$ ./bin/splitinput
JAN
FEB
MAR
APR
MAY
JUN
JUL
AUG
SEP
OCT
NOV
DEC

Hãy cho tôi biết nếu bạn có thêm bất kỳ câu hỏi nào.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.