सी में सीमांकक के साथ विभाजित स्ट्रिंग


155

मैं सी प्रोग्रामिंग भाषा में सीमांकक के साथ स्ट्रिंग के लिए एक सरणी को विभाजित और वापस करने के लिए एक फ़ंक्शन कैसे लिख सकता हूं?

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

25
आप strtokएक ही चीज़ को प्राप्त करने के लिए मानक लाइब्रेरी से फ़ंक्शन का उपयोग कर सकते हैं ।
डैनियल कामिल कोजार


एक टिप्पणी ... एक strtok()पारिवारिक समारोह के लिए मुख्य बिंदु static variablesसी में समझ में आता है। यानी वे क्रमिक फ़ंक्शन कॉल के बीच कैसे व्यवहार करते हैं जिसमें उनका उपयोग किया जाता है। नीचे मेरा कोड देखें
fnisi

जवाबों:


165

आप strtok()एक स्ट्रिंग को विभाजित करने के लिए फ़ंक्शन का उपयोग कर सकते हैं (और उपयोग करने के लिए सीमांकक निर्दिष्ट करें)। ध्यान दें कि strtok()इसमें पारित स्ट्रिंग को संशोधित करेगा। यदि मूल स्ट्रिंग की आवश्यकता है तो कहीं और इसकी एक प्रति बनाएं और प्रतिलिपि को पास करें strtok()

संपादित करें:

उदाहरण (ध्यान दें कि यह लगातार सीमांकक नहीं संभालता है, "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]

60
नमस्ते! को मैन पेज में strtokobsoleted के रूप में चिह्नित किया गया strsep(3)है।
ऑग्क्स

4
जैसा कि इसके लिए स्टैक ओवरफ्लो पर विहित प्रश्न / उत्तर हो सकता है, क्या स्ट्रैटोक का उपयोग करके मल्टी-थ्रेडिंग के संबंध में कुछ चेतावनी नहीं हैं?
पीटर मोर्टेंसन

3
@osgx उस पेज के अनुसार, के strsepलिए एक प्रतिस्थापन है strtok, लेकिन strtokपोर्टेबिलिटी के लिए पसंद किया जाता है। इसलिए, जब तक आपको खाली खेतों के लिए समर्थन की आवश्यकता नहीं होती है या एक साथ कई तारों को विभाजित करना strtokबेहतर विकल्प होता है।

4
@ डोजो: यह याद है; यह एक कारण है कि यह समस्याग्रस्त है। सादे की तुलना में strtok_s()(Microsoft, C11 अनुलग्नक K, वैकल्पिक) या strtok_r()(POSIX) का उपयोग करना बेहतर होगा strtok()strtok()एक पुस्तकालय समारोह में सादा बुराई है। लाइब्रेरी फ़ंक्शन को कॉल करने वाला कोई फ़ंक्शन strtok()उस समय उपयोग नहीं किया जा सकता है , और लाइब्रेरी फ़ंक्शन द्वारा कॉल किए जाने वाले कोई फ़ंक्शन कॉल नहीं कर सकता है strtok()
जोनाथन लेफ़लर

3
बस एक नोट जो strtok()थ्रेड सेफ नहीं है (@JonathanLeffler उल्लेखित कारणों से) और इसलिए यह पूरा फ़ंक्शन थ्रेड सुरक्षित नहीं है। यदि आप इसे एक खराब वातावरण में उपयोग करने की कोशिश करते हैं, तो आपको अनिश्चित और अप्रत्याशित परिणाम मिलेंगे। इस समस्या strtok()को strtok_r()हल करने के लिए प्रतिस्थापित करना ।
सीन डब्ल्यू

70

मुझे लगता 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's memory now.
while ((token = strsep(&str, ","))) my_fn(token);
free(tofree);

यदि दो सीमांकक एक साथ दिखाई देते हैं str, तो आपको एक tokenमान मिलेगा जो खाली स्ट्रिंग है। मूल्य strको संशोधित किया गया है कि प्रत्येक सीमांकक को शून्य बाइट के साथ अधिलेखित किया गया है - स्ट्रिंग को पहले पार्स किए जाने का एक और अच्छा कारण है।

एक टिप्पणी में, किसी ने सुझाव दिया कि अधिक पोर्टेबल strtokहोने के strsepकारण बेहतर है strtok। उबंटू और मैक ओएस एक्स है strsep; यह अनुमान लगाना सुरक्षित है कि अन्य यूनिक्स सिस्टम भी ऐसा करते हैं। विंडोज में कमी है 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 बनाम की एक अच्छी व्याख्या है strtok। पेशेवरों और विपक्ष को विषय के आधार पर आंका जा सकता है; हालाँकि, मुझे लगता है कि यह एक ऐसा संकेत है जिसे strsepएक प्रतिस्थापन के रूप में डिजाइन किया गया था strtok


3
पोर्टेबिलिटी पर अधिक सटीक: यह POSIX 7 नहीं है , लेकिन BSD व्युत्पन्न है, और glibc पर लागू किया गया है ।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

मैं बस पूछने ही वाला था ... पेले की सी का स्ट्रैपअप () है, लेकिन कोई स्ट्रैसप () नहीं है।
rdtsc

1
क्यों tofreeएक free'd और नहीं है str?
सडालियन

1
आप मुक्त नहीं कर सकते strक्योंकि इसके मूल्य को कॉल द्वारा बदला जा सकता है strsep()। जिस tofreeमेमोरी को आप फ्री करना चाहते हैं, उसके शुरू होने के लिए लगातार पॉइंट का मूल्य ।
टायलर

26

स्ट्रिंग टोकन इस कोड को आपको सही दिशा में लाना चाहिए।

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

नीचे दी गई विधि आपके लिए सभी कार्य (मेमोरी आवंटन, लंबाई की गणना) करेगी। अधिक जानकारी और विवरण यहां पाया जा सकता है - जावा स्ट्रिंग के विभाजन के लिए String.split () विधि का कार्यान्वयन

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

4
हुह थ्री स्टार प्रोग्रामर :)) यह दिलचस्प लगता है।
मिक्सी

जब मैं ऐसा करता हूं, यह या तो पिछले टोकन में बहुत अधिक जोड़ता है, या इसे बहुत अधिक मेमोरी आवंटित करता है। यह आउटपुट है: 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
इस उदाहरण में कई मेमोरी लीक हैं। इसे पढ़ने वाले किसी भी व्यक्ति के लिए, इस दृष्टिकोण का उपयोग न करें। इसके बजाय strtok या strsep टोकन दृष्टिकोण को प्राथमिकता दें।
जोर्मा रेबेन

7

यहाँ मेरे दो सेंट हैं:

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

3
ओह बोई, तीन पॉइंटर्स! मैं पहले से ही इसका उपयोग करने से डरता हूं कि यह सिर्फ मेरे लिए है, मैं सी में पॉइंटर्स के साथ बहुत अच्छा नहीं हूं।
हाफिज तेमुरी

धन्यवाद आदमी, उपरोक्त सभी जवाबों ने प्रयासों के बाद भी मेरे मामले में काम नहीं किया, और आपका कोड एक आकर्षण की तरह काम करता है!
हम्फ़गट

4

उपरोक्त उदाहरण में, स्ट्रिंग में जगह में अशक्त समाप्त स्ट्रिंग (जैसे आप चाहते हैं) की एक सरणी को वापस करने का एक तरीका होगा। हालांकि, शाब्दिक स्ट्रिंग को पारित करना संभव नहीं होगा, क्योंकि इसे फ़ंक्शन द्वारा संशोधित करना होगा:

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

ऐसा करने के लिए शायद एक भोला तरीका है, लेकिन आपको यह विचार मिलता है।


3

यह फ़ंक्शन एक चार * स्ट्रिंग लेता है और इसे डेलिमिनेटर द्वारा विभाजित करता है। एक पंक्ति में कई अपराधी हो सकते हैं। ध्यान दें कि फ़ंक्शन ओरिजिनल स्ट्रिंग को संशोधित करता है। आपको मूल स्ट्रिंग की एक प्रति पहले बनानी होगी, अगर आपको अनवांटेड रहने के लिए मूल की आवश्यकता हो। यह फ़ंक्शन किसी भी cstring फ़ंक्शन कॉल का उपयोग नहीं करता है, इसलिए यह दूसरों की तुलना में थोड़ा तेज़ हो सकता है। यदि आप मेमोरी आवंटन के बारे में परवाह नहीं करते हैं, तो आप फ़ंक्शन के शीर्ष पर sub_strings आवंटित कर सकते हैं आकार के साथ strlen (src_str) / 2 और (जैसे c ++ "संस्करण" उल्लेख किया गया है) फ़ंक्शन के निचले आधे हिस्से को छोड़ दें। यदि आप ऐसा करते हैं, तो फ़ंक्शन O (N) तक कम हो जाता है, लेकिन नीचे दिखाया गया मेमोरी ऑप्टिमाइज़ेड तरीका O (2N) है।

कार्यक्रम:

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

इसे कैसे उपयोग करे:

  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

नीचे zString लाइब्रेरीstrtok() से मेरा कार्यान्वयन है । मानक पुस्तकालय से अलग है जिस तरह से यह लगातार सीमांकक व्यवहार करता है।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 से डाउनलोड किया जा सकता है


अच्छा है! यही मैं ढूंढ रहा था।
कोस्तिया किम

3

मुझे लगता है कि निम्नलिखित समाधान आदर्श है:

  • स्रोत स्ट्रिंग को नष्ट नहीं करता है
  • री-एंट्रेंट - यानी, आप इसे सुरक्षित रूप से एक या अधिक थ्रेड्स में कहीं से भी कॉल कर सकते हैं
  • पोर्टेबल
  • कई विभाजकों को सही ढंग से संभालता है
  • तेज और कुशल

कोड की व्याख्या:

  1. एक संरचना को परिभाषित करें tokenटोकनों के पते और लंबाई को संग्रहीत करने के लिए को
  2. सबसे खराब स्थिति में इन के लिए पर्याप्त मेमोरी आवंटित करें, जो तब strहै जब पूरी तरह से विभाजक से बना हैstrlen(str) + 1 टोकन हैं, सभी खाली तार होते हैं
  3. स्कैन str पते और हर टोकन की लंबाई रिकॉर्डिंग
  4. सही आकार के आउटपुट सरणी को आवंटित करने के लिए इसका उपयोग करें, जिसमें एक के लिए अतिरिक्त स्थान भी शामिल है NULL सेंटिनल मान के
  5. प्रारंभ और लंबाई की जानकारी का उपयोग करके टोकन आवंटित करें, कॉपी करें और जोड़ें - memcpyजैसे कि यह तेजी से उपयोग होता हैstrcpy और हम लंबाई जानते हैं
  6. मुफ्त टोकन का पता और लंबाई सरणी
  7. टोकन की सरणी लौटाएं
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 *एक स्प्लिट फ़ंक्शन से पॉइंटर्स की एक सरणी को इस तरह नहीं लौटाता क्योंकि यह कॉलर पर बहुत सारी जिम्मेदारी देता है ताकि उन्हें सही ढंग से मुक्त कर सके। एक इंटरफ़ेस जो मैं पसंद करता हूं, वह है कॉल करने वाले को कॉलबैक फ़ंक्शन को पास करने की अनुमति देना और हर टोकन के लिए यह कॉल करना, जैसा कि मैंने यहां बताया है: स्प्लिट ए स्ट्रिंग इन सी


विभाजकों के लिए दो बार स्कैन करना संभवतः संभावित बड़े सरणी को आवंटित करने की तुलना में अधिक उचित है token
20

2

यह प्रयोग करके देखें।

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

यह अनुकूलित विधि * परिणाम में संकेत के एक मौजूदा सरणी (या मौजूदा) को अद्यतन करती है और तत्वों की संख्या को * गिनती में लौटाती है।

आपके द्वारा अपेक्षित स्ट्रिंग की अधिकतम संख्या (जब आप किसी मौजूदा सरणी या किसी अन्य रेस्टन को निर्दिष्ट करते हैं) को इंगित करने के लिए "अधिकतम" का उपयोग करें, और 0 पर सेट करें

सीमांकक की सूची की तुलना करने के लिए, डेल को चार * के रूप में परिभाषित करें और लाइन को बदलें:

if (str[i]==delim) {

निम्नलिखित दो पंक्तियों के साथ:

 char *c=delim; while(*c && *c!=str[i]) c++;
 if (*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;
}  

उपयोग उदाहरण:

#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

मेरा संस्करण:

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

यह एक स्ट्रिंग स्प्लिटिंग फ़ंक्शन है जो मल्टी-कैरेक्टर डेलिमिटर को संभाल सकता है। ध्यान दें कि यदि सीमांकक स्ट्रिंग कि विभाजन किया जा रहा है से अधिक लंबी है, तो bufferऔर stringLengthsपर सेट हो जाएगा (void *) 0, और numStringsकरने के लिए स्थापित किया जाएगा 0

इस एल्गोरिथ्म का परीक्षण किया गया है, और काम करता है। (अस्वीकरण: यह गैर-एएससीआईआई स्ट्रिंग के लिए परीक्षण नहीं किया गया है, और यह मानता है कि कॉलर ने वैध पैरामीटर दिए हैं)

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>

मैं इसे मुख्य से कैसे बुलाऊं? मैं नहीं जानता कि बफर को क्या करना है।
आयमोन फोरनियर

आवंटन का तर्क गलत है। realloc () नया पॉइंटर लौटाता है और आप लौटाए गए मान को छोड़ देते हैं। नई मेमोरी पॉइंटर को वापस करने का कोई उचित तरीका नहीं है - फ़ंक्शन प्रोटोटाइप को आवंटित के आकार को स्वीकार करने bufferऔर कॉल करने वाले को आवंटन छोड़ने, अधिकतम आकार के तत्वों को संसाधित करने के लिए बदला जाना चाहिए ।
एलेक्स

@ एलेक्स फिक्स्ड, पूरी तरह से फिर से लिखा, और परीक्षण किया गया। नोट: यह सुनिश्चित नहीं है कि यह गैर-एएससीआईआई के लिए काम करेगा या नहीं।
49lektra

शुरुआत के लिए, यह सी कोड नहीं है। और आप C ++ में वास्तविक संदर्भ द्वारा पॉइंटर्स पास क्यों करेंगे?
कामीकोलो

@Kamiccolo मुझे क्षमा करें, यह वास्तव में C कोड नहीं है? इसके अलावा, यहाँ एक समस्या के संदर्भ में संकेत क्यों दे रहा है?
32lektra

1

मेरा दृष्टिकोण स्ट्रिंग को स्कैन करने और बिंदुओं को हर चरित्र को इंगित करने के बाद होता है जब अपराधी (और पहला चरित्र) के बाद, उसी समय स्ट्रिंगर में अपराधी के दिखावे को '0' में असाइन करें।
पहले मूल स्ट्रिंग की एक प्रति बनाएं (चूंकि यह स्थिर है), फिर इसे स्कैन करके स्प्लिट की संख्या प्राप्त करें इसे पॉइंटर पैरामीटर लेन में पास करें । उसके बाद, कॉपी स्ट्रिंग पॉइंटर के लिए पहले परिणाम सूचक को इंगित करें, फिर कॉपी स्ट्रिंग को स्कैन करें: एक बार एक अपराधी का सामना करने के बाद, इसे '\ 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;
}

यह तरीका गलत है। मुझे अभी इस पोस्ट को हटा दिया गया था, लेकिन तब मुझे एहसास हुआ कि यह आप में से कुछ के लिए दिलचस्प है।
मेटलक्रैश

1

मेरा कोड (परीक्षण किया गया):

#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

1
ज्ञात हो कि strtok फ़ंक्शन स्ट्रिंग को बदल देता है 'str' को लागू किया गया था!
Schlx

1

विस्फोट और प्रत्यारोपण - प्रारंभिक स्ट्रिंग बरकरार है, गतिशील मेमोरी आवंटन

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

0

यदि आप बाहरी पुस्तकालय का उपयोग करने के इच्छुक हैं, तो मैं अनुशंसा नहीं कर सकता bstrlib पर्याप्त । यह थोड़ा अतिरिक्त सेटअप लेता है, लेकिन लंबे समय में उपयोग करना आसान है।

उदाहरण के लिए, नीचे दिए गए स्ट्रिंग को विभाजित करें, पहले एक कॉल के bstringसाथ बनाता है bfromcstr()। (एक bstringचार बफर के आसपास एक आवरण है)। इसके बाद, स्ट्रिंग को अल्पविराम पर विभाजित करें, struct bstrListजिसके परिणामस्वरूप परिणाम एक है , जिसमें फ़ील्ड qtyऔर एक सरणी है entry, जो कि एस की एक सरणी है bstring

bstrlibपर संचालित करने के लिए कई अन्य कार्य करता है bstringरों

बहुत आसान...

#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

फिर भी एक और जवाब (यह यहाँ से यहाँ ले जाया गया था ):

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 : आप स्ट्रटोक के कुछ अन्य कार्यान्वयन का उपयोग विभिन्न पदों में उल्लिखित कारणों से कर सकते हैं।

आप इसका उपयोग कर सकते हैं जैसे:

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

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

(मैंने इसका परीक्षण नहीं किया, इसलिए कृपया मुझे बताएं कि क्या यह काम नहीं करता है!)


0

इस प्रश्न के दो मुद्दे हैं स्मृति प्रबंधन और थ्रेड सुरक्षा। जैसा कि आप कई पदों से देख सकते हैं, सी में मूल रूप से पूरा करने के लिए यह एक आसान काम नहीं है। मैं एक समाधान चाहता हूं जो है:

  • सुरक्षित धागा। (strtok धागा सुरक्षित नहीं है)
  • मॉलॉक या इसके किसी भी डेरिवेटिव को नियुक्त नहीं करता है (स्मृति प्रबंधन मुद्दों से बचने के लिए)
  • अलग-अलग फ़ील्ड्स पर चेक बाउंड्स चेक करता है (अज्ञात डेटा पर खंड दोष से बचने के लिए)
  • मल्टी-बाइट फ़ील्ड विभाजकों के साथ काम करता है (utf-8)
  • इनपुट में अतिरिक्त फ़ील्ड को अनदेखा करता है
  • अमान्य फ़ील्ड लंबाई के लिए नरम त्रुटि दिनचर्या प्रदान करता है

समाधान मैं आया इन सभी मानदंडों को पूरा करता है। यहां पोस्ट किए गए कुछ अन्य समाधानों की तुलना में शायद यह थोड़ा अधिक काम है, लेकिन मुझे लगता है कि व्यवहार में, अतिरिक्त समाधान अन्य समाधानों के सामान्य नुकसान से बचने के लिए इसके लायक है।

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

नीचे एक उदाहरण संकलन और आउटपुट है। ध्यान दें कि मेरे उदाहरण में, मैंने जानबूझकर "एपीआरआईएल" को वर्तनी दिया ताकि आप देख सकें कि नरम त्रुटि कैसे काम करती है।

$ 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

का आनंद लें!


0

यहां एक और कार्यान्वयन है जो एक स्ट्रिंग-शाब्दिक रूप से संचालित करने के लिए सुरक्षित रूप से संचालित होगा , जो प्रश्न में अनुरोध किए गए प्रोटोटाइप से मिलान करता है जो कि एक आवंटित पॉइंटर-टू-पॉइंटर को चार (जैसे char **) में लौटाता है । सीमांकक स्ट्रिंग में कई वर्ण हो सकते हैं, और इनपुट स्ट्रिंग में किसी भी संख्या में टोकन हो सकते हैं। सभी आवंटन और वसूली POSIX द्वारा mallocया उसके reallocबिना नियंत्रित किए जाते हैं strdup

आवंटित किए गए बिंदुओं की प्रारंभिक संख्या NPTRSनिरंतर द्वारा नियंत्रित की जाती है और केवल सीमा यह है कि यह शून्य से अधिक हो। char **लौटे एक शामिल प्रहरी NULL के बाद पिछले टोकन के समान *argv[]और द्वारा प्रपत्र प्रयोग करने योग्य में execv, execvpऔरexecve

जैसा कि strtok()कई अनुक्रमिक सीमांकक के रूप में एक एकल सीमांकक के रूप में व्यवहार किया जाता है, इसलिए "JAN,FEB,MAR,APR,MAY,,,JUN,JUL,AUG,SEP,OCT,NOV,DEC"मान लिया जाएगा कि केवल एक ही ','पृथक होता है "MAY,JUN"

नीचे दिए गए फ़ंक्शन में इन-लाइन टिप्पणी की गई है और main()महीनों को विभाजित करते हुए एक छोटा जोड़ा गया है। आवंटित किए गए पॉइंटर्स की प्रारंभिक संख्या 2इनपुट स्ट्रिंग को टोकेलाइज़ करने के दौरान तीन वास्तविककरण को बाध्य करने के लिए निर्धारित की गई थी :

#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 */
    }
}

उदाहरण का उपयोग करें / आउटपुट

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

और अधिक प्रश्न होने पर मुझसे पूछें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.