स्ट्रेटोक () स्ट्रिंग को C में टोकन में कैसे विभाजित करता है?


114

कृपया मुझे strtok()फंक्शन का काम समझाएं । मैनुअल कहता है कि यह स्ट्रिंग को टोकन में तोड़ देता है। मैं मैनुअल से यह समझने में असमर्थ हूं कि यह वास्तव में क्या करता है।

मैंने घड़ियों को जोड़ा strऔर *pchइसके काम की जांच करने के लिए जब पहली बार लूप हुआ, तो सामग्री strकेवल "यह" थी। नीचे दिखाया गया आउटपुट स्क्रीन पर कैसे छपा?

/* strtok example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] ="- This, a sample string.";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," ,.-");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
  }
  return 0;
}

आउटपुट:

विभाजन स्ट्रिंग "- यह, एक नमूना स्ट्रिंग।" टोकन में:
यह
ए
नमूना
तार

5
strtok()लौटने से पहले एनयूएल के साथ टोकन समाप्त करके अपने तर्क स्ट्रिंग को संशोधित करता है। यदि आप संपूर्ण बफ़र (str []) की जांच करने का प्रयास करते हैं, तो आप देखेंगे कि इसे लगातार कॉल के बीच संशोधित किया जा रहा है strtok()
माइकल फोकरिसिस

इसके बजाय देखने का str, घड़ी str[0], str[1], str[2], ...
पीएमजी

@pmg: मैंने str [0] देखा और str [1] .str [1] को '\ 0' होना चाहिए, लेकिन यह वहां एक स्पेस था।
फुद्दीन

3
ईमानदारी से मैंने कभी जांच करने की जहमत नहीं उठाई, लेकिन मुझे लगता है कि यह अंतिम पॉइंटर को स्टोर कर देता है, साथ ही यह उस स्थिति से भी बचेगा जिस पर इसे छोड़ा गया था। तब यह केवल जारी रह सकता है यदि सूचक NULL है, या स्थिति को साफ़ करें और यदि नहीं तो शुरू करें।
क्रिस

7
@ फ़ायरगंज: स्टैटिक वेरिएबल
DC20

जवाबों:


38

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


एक टोकन के लिए अंतिम स्थिति अगले टोकन का शुरुआती टोकन बन जाती है? क्या अंत स्थिति के स्थान पर एक शून्य चरित्र रखा गया है?
फुद्दीन

1
@ फहद- हां, आपके द्वारा किए गए सभी परिसीमन को एनयूएल चरित्र द्वारा बदल दिया जाएगा जैसा कि अन्य लोगों ने भी सुझाव दिया है।
सचिन शानबाग

यदि सभी परिसीमन को नुल द्वारा प्रतिस्थापित किया जाता है, तो स्ट्रिंग में "-थिस" क्यों होता है? इसमें
फडिन

2
@ इफ़हाद - यह केवल NUL के साथ सीमांकक वर्णों को बदलता है, न कि सभी वर्णों के बीच परिसीमन। स्ट्रिंग के कई प्रकारों में विभाजित होने का एक प्रकार है। आपको "यह" मिलता है क्योंकि इसके दो निर्दिष्ट सीमांकक के बीच है न कि "-थिस"।
सचिन शांभाग

1
@ फहद - हां, बिल्कुल। सभी रिक्त स्थान, "," और "-" को एनयूएल द्वारा प्रतिस्थापित किया जाता है क्योंकि आपने इन्हें सीमांकक के रूप में निर्दिष्ट किया है, जहां तक ​​मैं समझता हूं।
सचिन शानबाग

212

strtok रनटाइम फ़ंक्शन इस तरह काम करता है

पहली बार जब आप स्ट्रेटोक कहते हैं तो आप एक स्ट्रिंग प्रदान करते हैं जिसे आप टोकन देना चाहते हैं

char s[] = "this is a string";

उपरोक्त स्ट्रिंग स्थान में शब्दों के बीच एक अच्छा परिसीमन प्रतीत होता है, ताकि इसका उपयोग किया जा सके:

char* p = strtok(s, " ");

अब ऐसा होता है कि 's' तब तक खोजा जाता है जब तक कि अंतरिक्ष वर्ण नहीं मिल जाता है, पहला टोकन वापस आ जाता है ('यह') और उस टोकन (स्ट्रिंग) को इंगित करता है

अगले टोकन प्राप्त करने और उसी स्ट्रिंग के साथ जारी रखने के लिए NULL को पहले तर्क के रूप में पारित किया गया है क्योंकि strtok आपके पिछले पास किए गए स्ट्रिंग के लिए एक स्थिर सूचक रखता है :

p = strtok(NULL," ");

p अब 'है' की ओर इशारा करता है

और इसलिए जब तक कोई और स्थान नहीं मिल सकता है, तब तक अंतिम स्ट्रिंग अंतिम टोकन 'स्ट्रिंग' के रूप में वापस आ जाएगी।

और अधिक आसानी से आप इसे लिख सकते हैं इसके बजाय सभी टोकन का प्रिंट आउट करने के लिए:

for (char *p = strtok(s," "); p != NULL; p = strtok(NULL, " "))
{
  puts(p);
}

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

यदि आप लौटे हुए मानों को स्टोर करना चाहते हैं, तो आपको strtokटोकन को किसी अन्य बफर में कॉपी करना होगा, जैसे strdup(p);कि मूल स्ट्रिंग (अंदर स्थिर सूचक द्वारा इंगित strtok) को टोकन वापस करने के लिए पुनरावृत्तियों के बीच संशोधित किया गया है।


तो यह वास्तव में स्ट्रिंग के बीच एक शून्य चरित्र नहीं रखता है? मेरी घड़ी क्यों दिखाती है कि स्ट्रिंग केवल "THIS" के साथ छोड़ दी गई है?
फुद्दीन

4
यह वास्तव में '' को '' 0 '' के साथ मिला है। और, यह बाद में बहाल नहीं करता है, इसलिए आपकी स्ट्रिंग अच्छे के लिए बर्बाद हो गई है।

33
स्थैतिक बफर के लिए +1, यह वही है जो मुझे समझ में नहीं आया
IEatBagels

1
एक बहुत ही महत्वपूर्ण विवरण, "पहली टोकन वापस आ गई है और pउस टोकन को इंगित करता है" रेखा से गायब है , यह है कि strtokमूल स्ट्रिंग को एक सीमांकक के स्थान पर एक अशक्त वर्णों को म्यूट करने की आवश्यकता है (अन्यथा अन्य स्ट्रिंग फ़ंक्शन पता नहीं होगा, जहां टोकन समाप्त होता है)। और यह स्टेटिक वैरिएबल का उपयोग करके स्टेट का ट्रैक भी रखता है।
ग्रू

@ मुझे लगता है कि मैंने पहले ही 2017 में जो एडिट किया था, उसमें जोड़ दिया, लेकिन आप सही हैं।
एंडर्सक

25

strtokस्ट्रिंग में अगले उपलब्ध टोकन की ओर इशारा करते हुए एक स्थिर, आंतरिक संदर्भ रखता है; यदि आप इसे NULL पॉइंटर पास करते हैं, तो यह उस आंतरिक संदर्भ से काम करेगा।

यही कारण strtokहै कि दोबारा प्रवेश नहीं होता है; जैसे ही आप इसे एक नया पॉइंटर पास करते हैं, वह पुराना आंतरिक संदर्भ क्लोब हो जाता है।


पुराने आंतरिक संदर्भ 'क्लोबबर्ड' होने का क्या मतलब है। क्या आपका मतलब 'ओवरराइट' है?
ylun.ca

1
@ ylun.ca: हाँ, मेरा यही मतलब है।
जॉन बोड

10

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

POSIX strtokपृष्ठ से:

यह फ़ंक्शन कॉल के बीच वर्तमान स्ट्रिंग स्थिति का ट्रैक रखने के लिए स्थिर भंडारण का उपयोग करता है।

एक थ्रेड-सुरक्षित संस्करण ( strtok_r) है जो इस प्रकार का जादू नहीं करता है।


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

यह गलत है: " strtokपैरामीटर खुद को नहीं बदलता है ( str)।" puts(str);प्रिंट "- यह" strtokसंशोधित के बाद से str
MarredCheese

1
@MarredCheese: फिर से पढ़ें। यह पॉइंटर को संशोधित नहीं करता है। यह डेटा को पॉइंटर पॉइंट (यानी स्ट्रिंग डेटा) को संशोधित करता है
Mat

ओह ठीक है, मुझे महसूस नहीं हुआ कि आप क्या कर रहे हैं। माना।
मैरेडेकेस

8

पहली बार जब आप इसे कॉल करते हैं, तो आप स्ट्रिंग को टोकन प्रदान करते हैं strtok। और फिर, निम्न टोकन प्राप्त करने के लिए, आप बस NULLउस फ़ंक्शन को देते हैं, जब तक कि वह एक नॉन NULLपॉइंटर लौटाता है ।

strtokसमारोह स्ट्रिंग आप पहली बार प्रदान की है जब आप इसे कहते रिकॉर्ड करता है। (जो बहु-सूत्र अनुप्रयोगों के लिए वास्तव में खतरनाक है)


8

strtok एक स्ट्रिंग को टोकन करेगा अर्थात इसे सबस्ट्रिंग की श्रृंखला में परिवर्तित करेगा।

यह ऐसा करता है कि इन टोकन (या सबस्ट्रिंग) को अलग करने वाले सीमांकक की खोज करके। और आप सीमांकक निर्दिष्ट करते हैं। आपके मामले में, आप '' या ',' या 'चाहते हैं।' या '-' का परिसीमन होना।

इन टोकनों को निकालने के लिए प्रोग्रामिंग मॉडल यह है कि आप अपने मुख्य स्ट्रिंग और सीमांकक के सेट को आगे बढ़ाएं। फिर आप इसे बार-बार कॉल करते हैं, और हर बार strtok इसे ढूंढता हुआ अगला टोकन लौटाएगा। जब तक यह मुख्य स्ट्रिंग के अंत तक नहीं पहुंचता है, जब यह एक अशक्त देता है। एक और नियम यह है कि आप केवल पहली बार में स्ट्रिंग पास करते हैं, और बाद के समय के लिए NULL। यह स्ट्रेटोक बताने का एक तरीका है यदि आप एक नए स्ट्रिंग के साथ टोकन का एक नया सत्र शुरू कर रहे हैं, या आप पिछले टोकन सत्र से टोकन प्राप्त कर रहे हैं। ध्यान दें कि strtok टोकन सत्र के लिए अपने राज्य को याद करता है। और इस कारण से यह रीफ्रेंट या थ्रेड सुरक्षित नहीं है (आपको इसके बजाय strtok_r का उपयोग करना चाहिए)। एक और बात यह है कि यह वास्तव में मूल स्ट्रिंग को संशोधित करता है। यह तेह सीमांकक के लिए '\ 0' लिखता है जो इसे पाता है।

स्ट्रैटोक आह्वान करने का एक तरीका, संक्षेप में, इस प्रकार है:

char str[] = "this, is the string - I want to parse";
char delim[] = " ,-";
char* token;

for (token = strtok(str, delim); token; token = strtok(NULL, delim))
{
    printf("token=%s\n", token);
}

परिणाम:

this
is
the
string
I
want
to
parse

5

strtok अपने इनपुट स्ट्रिंग को संशोधित करता है। यह इसमें अशक्त वर्ण ('\ 0') रखता है ताकि यह मूल स्ट्रिंग के बिट्स को टोकन के रूप में वापस कर देगा। वास्तव में स्ट्रेटोक मेमोरी आवंटित नहीं करता है। आप इसे बेहतर समझ सकते हैं यदि आप स्ट्रिंग को बक्से के अनुक्रम के रूप में खींचते हैं।


3

यह समझने के लिए कि कैसे strtok()काम करता है, सबसे पहले यह जानने की जरूरत है कि एक स्थिर चर क्या है। यह लिंक इसे काफी अच्छी तरह से समझाता है…।

के संचालन के लिए महत्वपूर्ण है, अलग- strtok()अलग कॉल के बीच आखिरी सेपरेटर के स्थान को संरक्षित करना (इसीलिए strtok()यह बहुत मूल स्ट्रिंग को पार्स करना जारी रखता है जो इसे पास किया जाता है जब इसे एक के साथ लगाया जाता हैnull pointer क्रमिक कॉल के ) ।।

मेरे स्वयं के strtok()कार्यान्वयन पर एक नज़र डालें , जिसे कहा जाता है zStrtok(), जिसके द्वारा प्रदान किए गए की तुलना में एक अलग तरह की कार्यक्षमता हैstrtok()

char *zStrtok(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",zStrtok(s,","));
      printf("2 %s\n",zStrtok(NULL,","));
      printf("3 %s\n",zStrtok(NULL,","));
      printf("4 %s\n",zStrtok(NULL,","));
      printf("5 %s\n",zStrtok(NULL,","));
      printf("6 %s\n",zStrtok(NULL,","));

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

कोड एक स्ट्रिंग प्रोसेसिंग लाइब्रेरी से है जिसे मैं जीथब पर बनाए रखता हूं , जिसे zString कहा जाता है। कोड पर एक नज़र डालें, या यहां तक ​​कि योगदान दें :) https://github.com/fnoyanisi/zString


3

यह है कि मैंने स्ट्रेटोक कैसे लागू किया, यह बहुत अच्छा नहीं है, लेकिन इस पर 2 घंटे काम करने के बाद आखिरकार यह काम कर गया। यह कई सीमांकक का समर्थन करता है।

#include "stdafx.h"
#include <iostream>
using namespace std;

char* mystrtok(char str[],char filter[]) 
{
    if(filter == NULL) {
        return str;
    }
    static char *ptr = str;
    static int flag = 0;
    if(flag == 1) {
        return NULL;
    }
    char* ptrReturn = ptr;
    for(int j = 0; ptr != '\0'; j++) {
        for(int i=0 ; filter[i] != '\0' ; i++) {
            if(ptr[j] == '\0') {
                flag = 1;
                return ptrReturn;
            }
            if( ptr[j] == filter[i]) {
                ptr[j] = '\0';
                ptr+=j+1;
                return ptrReturn;
            }
        }
    }
    return NULL;
}

int _tmain(int argc, _TCHAR* argv[])
{
    char str[200] = "This,is my,string.test";
    char *ppt = mystrtok(str,", .");
    while(ppt != NULL ) {
        cout<< ppt << endl;
        ppt = mystrtok(NULL,", ."); 
    }
    return 0;
}


1

यहां मेरा कार्यान्वयन है जो सीमांकक के लिए हैश तालिका का उपयोग करता है, जिसका अर्थ है कि यह ओ (एन) 2 के बजाय ओ (एन) है (यहां कोड की एक कड़ी है) :

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

#define DICT_LEN 256

int *create_delim_dict(char *delim)
{
    int *d = (int*)malloc(sizeof(int)*DICT_LEN);
    memset((void*)d, 0, sizeof(int)*DICT_LEN);

    int i;
    for(i=0; i< strlen(delim); i++) {
        d[delim[i]] = 1;
    }
    return d;
}



char *my_strtok(char *str, char *delim)
{

    static char *last, *to_free;
    int *deli_dict = create_delim_dict(delim);

    if(!deli_dict) {
        /*this check if we allocate and fail the second time with entering this function */
        if(to_free) {
            free(to_free);
        }
        return NULL;
    }

    if(str) {
        last = (char*)malloc(strlen(str)+1);
        if(!last) {
            free(deli_dict);
            return NULL;
        }
        to_free = last;
        strcpy(last, str);
    }

    while(deli_dict[*last] && *last != '\0') {
        last++;
    }
    str = last;
    if(*last == '\0') {
        free(deli_dict);
        free(to_free);
        deli_dict = NULL;
        to_free = NULL;
        return NULL;
    }
    while (*last != '\0' && !deli_dict[*last]) {
        last++;
    }

    *last = '\0';
    last++;

    free(deli_dict);
    return str;
}

int main()
{
    char * str = "- This, a sample string.";
    char *del = " ,.-";
    char *s = my_strtok(str, del);
    while(s) {
        printf("%s\n", s);
        s = my_strtok(NULL, del);
    }
    return 0;
}

1

strtok () सूचक को स्थिर चर में संग्रहीत करता है जहां आपने पिछली बार छोड़ दिया था, इसलिए इसकी दूसरी कॉल पर, जब हम अशक्त गुजरते हैं, तो strtok () को स्थिर चर से सूचक मिलता है।

यदि आप एक ही स्ट्रिंग नाम प्रदान करते हैं, तो यह फिर से शुरू से शुरू होता है।

इसके अलावा स्ट्रैटोक () विनाशकारी है यानी यह ओरेंजल स्ट्रिंग में बदलाव करता है। इसलिए सुनिश्चित करें कि आपके पास हमेशा एक प्रति की एक प्रति है।

स्ट्रैटोक () का उपयोग करने की एक और समस्या यह है कि चूंकि यह स्थिर चर में पता संग्रहीत करता है, इसलिए मल्टीट्र्रेड प्रोग्रामिंग कॉलिंग स्ट्रटोक () में एक से अधिक बार त्रुटि का कारण होगा। इसके लिए strtok_r () का उपयोग करें।


0

जिन लोगों को अभी भी इस strtok()फ़ंक्शन को समझने में कठिन समय हो रहा है , उनके लिए इस पाइथोण्टूटोर उदाहरण पर एक नज़र डालें , यह आपके सी (या सी ++, पायथन ...) कोड की कल्पना करने के लिए एक महान उपकरण है।

यदि लिंक टूट गया है, तो इसमें पेस्ट करें:

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

int main()
{
    char s[] = "Hello, my name is? Matthew! Hey.";
    char* p;
    for (char *p = strtok(s," ,?!."); p != NULL; p = strtok(NULL, " ,?!.")) {
      puts(p);
    }
    return 0;
}

क्रेडिट्स एंडर्स के।


0

यदि आप टोकन ढूंढ रहे हैं तो आप चार्ट को स्कैन कर सकते हैं यदि आपको यह पता चला है कि नई लाइन प्रिंट करें अन्यथा चार्ट प्रिंट करें।

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

int main()
{
    char *s;
    s = malloc(1024 * sizeof(char));
    scanf("%[^\n]", s);
    s = realloc(s, strlen(s) + 1);
    int len = strlen(s);
    char delim =' ';
    for(int i = 0; i < len; i++) {
        if(s[i] == delim) {
            printf("\n");
        }
        else {
            printf("%c", s[i]);
        }
    }
    free(s);
    return 0;
}

0

तो, यह इस विषय को बेहतर ढंग से समझने में मदद करने के लिए एक कोड स्निपेट है।

प्रिंट करने वाले टोकन

कार्य: एक वाक्य को देखते हुए, वाक्य के प्रत्येक शब्द को एक नई पंक्ति में प्रिंट करते हैं।

char *s;
s = malloc(1024 * sizeof(char));
scanf("%[^\n]", s);
s = realloc(s, strlen(s) + 1);
//logic to print the tokens of the sentence.
for (char *p = strtok(s," "); p != NULL; p = strtok(NULL, " "))
{
    printf("%s\n",p);
}

इनपुट: How is that

परिणाम:

How
is
that

स्पष्टीकरण: तो यहाँ, "strtok ()" फ़ंक्शन का उपयोग किया गया है और इसे अलग-अलग लाइनों में टोकन प्रिंट करने के लिए लूप का उपयोग करके पुनरावृत्त किया गया है।

फ़ंक्शन मापदंडों को 'स्ट्रिंग' और 'ब्रेक-पॉइंट' के रूप में ले जाएगा और उन ब्रेक-पॉइंट और फॉर्म टोकन पर स्ट्रिंग को तोड़ देगा। अब, उन टोकन को 'पी' में संग्रहीत किया जाता है और छपाई के लिए आगे उपयोग किया जाता है।


मुझे लगता है कि एक उदाहरण के माध्यम से व्याख्या करना कुछ डॉक्टर का उल्लेख करने से बेहतर है।
tr_abhishek
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.