সিতে ডিলিমিটারগুলির সাথে স্ট্রিট বিভক্ত করুন


155

সি প্রোগ্রামিং ল্যাঙ্গুয়েজে ডিলিমিটারগুলির সাথে স্ট্রিংয়ের জন্য একটি অ্যারেরকে বিভক্ত করতে এবং ফেরত দেওয়ার জন্য আমি কীভাবে একটি ফাংশন লিখব?

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

25
strtokএকই জিনিস অর্জনের জন্য আপনি স্ট্যান্ডার্ড লাইব্রেরি থেকে ফাংশনটি ব্যবহার করতে পারেন ।
ড্যানিয়েল কামিল কোজার


একটি মন্তব্য ... একটি strtok()পরিবারের ক্রিয়াকলাপের মূল পয়েন্টটি static variablesসিটিতে বোঝা যাচ্ছে success অর্থাত্ তারা ব্যবহার করা ক্রমাগত ফাংশন কলের মধ্যে তারা কীভাবে আচরণ করে। আমার কোডটি নীচে দেখুন
ফিনিস

উত্তর:


165

আপনি strtok()স্ট্রিংকে বিভক্ত করতে ফাংশনটি ব্যবহার করতে পারেন (এবং ব্যবহার করার জন্য ডিলিমিটার নির্দিষ্ট করুন)। নোট যা এতে strtok()প্রবেশ করা স্ট্রিংটি সংশোধন করবে। যদি অন্যদিকে মূল স্ট্রিংয়ের প্রয়োজন হয় তবে এর একটি অনুলিপি তৈরি করুন এবং অনুলিপিটি এতে পাস করুন strtok()

সম্পাদনা করুন:

উদাহরণ (নোট করুন এটি ক্রমাগত ডিলিমিটারগুলি পরিচালনা করে না, উদাহরণস্বরূপ "জ্যান ,,,, ফিবি, এমএআর"):

#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
ওহে! strtokযেমন দ্বারা অবচিত চিহ্নিত করা হয় strsep(3)মানুষ পৃষ্ঠা থেকে।
osgx

4
যেহেতু এটি স্ট্যাক ওভারফ্লোতে এর প্রাসঙ্গিক প্রশ্ন / উত্তর হতে পারে, স্ট্রটোক ব্যবহার করে মাল্টি-থ্রেডিংয়ের বিষয়ে কিছু সতর্কতা নেই?
পিটার মর্টেনসেন

3
@osgx এই পৃষ্ঠা অনুসারে, strsepএটি প্রতিস্থাপন strtok, তবে strtokবহনযোগ্যতার জন্য পছন্দসই। সুতরাং, যদি না খালি ক্ষেত্রের জন্য সমর্থন বা একসাথে একাধিক স্ট্রিং বিভক্ত করা প্রয়োজন, তবে strtokএটি আরও ভাল পছন্দ।

4
@ ডোজো: এটি মনে আছে; এটি সমস্যাযুক্ত কারণগুলির মধ্যে একটি। প্লেনের চেয়ে strtok_s()(মাইক্রোসফ্ট, সি 11 এনেক্স কে, alচ্ছিক) বা strtok_r()(পসিক্স) ব্যবহার করা ভাল strtok()। সাধারণ strtok()একটি লাইব্রেরির কার্যক্রমে খারাপ। লাইব্রেরির ক্রিয়াকলাপ কল করার কোনও ফাংশন strtok()সেই সময় ব্যবহার করা হতে পারে এবং লাইব্রেরি ফাংশন দ্বারা ডাকা কোনও ফাংশন কল করতে পারে না strtok()
জোনাথন লেফলার

3
strtok()থ্রেড নিরাপদ নয় এমন একটি নোট (যে কারণে জোনাথনলফলার উল্লেখ করেছেন) এবং সুতরাং এই পুরো ফাংশনটি থ্রেড নিরাপদ নয়। যদি আপনি এটি কোনও ট্র্যাডেড পরিবেশে ব্যবহার করার চেষ্টা করেন তবে আপনি অনারত এবং অনির্দেশ্য ফলাফল পাবেন। এই সমস্যার সমাধানের 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 না কিন্তু বাসদ উদ্ভূত, এবং বাস্তবায়িত জন্য glibc
সিরো সান্তিলি 20 冠状 病 六四 事件 法轮功

আমি তখনই জিজ্ঞাসা করতে যাচ্ছিলাম ... পেলের সি-তে স্টার্ডআপ () রয়েছে, তবে স্টার্সপ () নেই।
rdtsc

1
কেন tofreeএকজন মুক্ত এবং না str?
Sdlion

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

নীচের পদ্ধতিটি আপনার জন্য সমস্ত কাজ (মেমরি বরাদ্দ, দৈর্ঘ্য গণনা) করবে। আরও তথ্য এবং বিবরণ এখানে পাওয়া যাবে - সি স্ট্রিং বিভক্ত করার জন্য জাভা স্ট্রিং.স্প্লিট () পদ্ধতির প্রয়োগ

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.¢
কেজারহর্ম

2
এই উদাহরণে একাধিক মেমরি ফাঁস রয়েছে। যে কেউ এটি পড়ছেন, এই পদ্ধতির ব্যবহার করবেন না। পরিবর্তে স্ট্রটোক বা স্টার্স্প টোকনাইজেশন পদ্ধতির পছন্দ করুন।
জোর্মা রেবেনে

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
ওহ বোই, তিন পয়েন্টার! আমি এটি ইতিমধ্যে আমার কেবলমাত্র এটি ব্যবহার করতে ভীত হয়েছি, সি এর পয়েন্টারগুলির সাথে আমি খুব ভাল নই।
হাফিজ তেমুরি

ধন্যবাদ ম্যান, উপরের সমস্ত স্ট্রোক জবাব আমার ক্ষেত্রে প্রচুর পরিশ্রমের পরেও কার্যকর হয়নি, এবং আপনার কোডটি কবজির মতো কাজ করে!
hmmftg

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

এই ফাংশনটি একটি চর * স্ট্রিং নেয় এবং এটিকে ডিলিমিনেটর দ্বারা বিভক্ত করে। পরপর একাধিক ডিলিমিনেটর থাকতে পারে। নোট করুন যে ফাংশনটি অরিগনাল স্ট্রিংকে পরিবর্তন করে। আনলটার্টারড থাকার জন্য যদি আপনার আসলটির প্রয়োজন হয় তবে আপনাকে অবশ্যই প্রথমে মূল স্ট্রিংয়ের একটি অনুলিপি তৈরি করতে হবে। এই ফাংশনটি কোনও সিস্ট্রিং ফাংশন কল ব্যবহার করে না তাই এটি অন্যদের চেয়ে কিছুটা দ্রুত হতে পারে। আপনি যদি মেমরি বরাদ্দ সম্পর্কে চিন্তা না করেন তবে আপনি ফাংশনের শীর্ষে সাব_স্ট্রিংগুলি আকার স্ট্রেন (src_str) / 2 দিয়ে বরাদ্দ করতে পারেন (যেমন সি ++ "সংস্করণ" উল্লিখিত) ফাংশনের নীচের অর্ধেকটি এড়িয়ে যান। আপনি যদি এটি করেন তবে ফাংশনটি ও (এন) এ হ্রাস পেয়েছে, তবে নীচে দেখানো মেমরি অপ্টিমাইজড উপায় হ'ল ও (2 এন)।

কাজ:

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)

গ্রন্থাগারটি গিথুব 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
chqrlie

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

এই অ্যালগরিদম পরীক্ষা করা হয়েছে, এবং কাজ করে। (অস্বীকৃতি: এটি 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>

আমি কীভাবে এটিকে কল করব? আমি বাফারে কী পাস করব জানি না।
আইমন ফর্নিয়ার

বরাদ্দ যুক্তি ভুল। রিলোক () নতুন পয়েন্টার দেয় এবং আপনি প্রত্যাশিত মানটি বাতিল করেন। নতুন মেমোরি পয়েন্টারটি ফেরত দেওয়ার কোনও সঠিক উপায় নেই - বরাদ্দকৃত আকার গ্রহণ করতে bufferএবং কলার, প্রক্রিয়া সর্বাধিক আকারের উপাদানগুলিতে ছেড়ে দেওয়ার জন্য ফাংশন প্রোটোটাইপ পরিবর্তন করা উচিত ।
অ্যালেক্স

@ অ্যালেক্স ফিক্সড, সম্পূর্ণ নতুন করে লেখা এবং পরীক্ষিত। দ্রষ্টব্য: এটি ASCII বিহীন হয়ে কাজ করবে কিনা তা নিশ্চিত নয়।
klektra

প্রারম্ভিকদের জন্য, এটি সি কোড নয়। এবং কেন আপনি সি ++ এ প্রকৃত রেফারেন্স দিয়ে পয়েন্টারগুলি পাস করবেন?
কামিকোলো

@ কমিকোলো আমি দুঃখিত, এটি সি কোডটি ঠিক কীভাবে নয়? এছাড়াও, এখানে কেন রেফারেন্স দিয়ে পয়েন্টারগুলি পাস করা একটি সমস্যা?
klektra

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

বিস্ফোরণ ও ইমপ্লোড - প্রাথমিক স্ট্রিং অক্ষত, গতিশীল মেমরি বরাদ্দ remains

#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এস এর অ্যারে ।

bstrlibbstringএস এ পরিচালনা করার জন্য আরও অনেকগুলি ফাংশন রয়েছে

পাই হিসাবে সহজ ...

#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

তবুও অন্য উত্তর (এটি এখান থেকে এখানে সরানো হয়েছিল ):

স্ট্রোকক ফাংশনটি ব্যবহার করার চেষ্টা করুন:

এখানে বা এখানে এই বিষয়ে বিশদটি দেখুন

এখানে সমস্যাটি হ'ল আপনাকে 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);

}

দ্রষ্টব্য : বরাদ্দের সমস্যা এড়াতে আমরা একই লুপ এবং ফাংশনটি গণনা (এক পাস) গণনা করতে এবং অনুলিপিগুলি (দুটি পাস) তৈরির জন্য ব্যবহার করি order

দ্রষ্টব্য 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

এই প্রশ্নের চারপাশে দুটি বিষয় মেমরি পরিচালনা এবং থ্রেড সুরক্ষা। আপনি অসংখ্য পোস্ট থেকে দেখতে পাচ্ছেন যে সি তে নির্বিঘ্নে কাজ করা সহজ কাজ নয়, আমি একটি সমাধান চেয়েছিলাম যা হল:

  • থ্রেড নিরাপদ. (স্ট্রোকক থ্রেড নিরাপদ নয়)
  • ম্যালোক বা এর কোনও ডেরাইভেটিভ নিয়োগ করে না (মেমরি পরিচালনার সমস্যাগুলি এড়াতে)
  • স্বতন্ত্র ক্ষেত্রগুলিতে অ্যারে সীমা পরীক্ষা করে (অজানা ডেটাতে বিভাগের ত্রুটিগুলি এড়ানোর জন্য)
  • মাল্টি-বাইট ফিল্ড বিভাজক (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 **) ফেরত দেয় returning ডিলিমিটার স্ট্রিংয়ে একাধিক অক্ষর থাকতে পারে এবং ইনপুট স্ট্রিংটিতে টোকেনের সংখ্যা থাকতে পারে। সমস্ত বরাদ্দ এবং পুনঃনির্ধারণগুলি পসিক্স 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

আপনার আরও কিছু প্রশ্ন থাকলে আমাকে জানান know

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.