Programing

strtok ()는 C에서 문자열을 토큰으로 어떻게 분할합니까?

lottogame 2020. 8. 18. 08:09
반응형

strtok ()는 C에서 문자열을 토큰으로 어떻게 분할합니까?


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

산출:

문자열 "-이것은 샘플 문자열입니다." 토큰으로 :
견본

strtok()문자열을 토큰으로 나눕니다. 즉, 구분 기호 중 하나에서 다음으로 시작하면 하나의 토큰이됩니다. 귀하의 경우 시작 토큰은 "-"에서 시작되고 다음 공백 ""으로 끝납니다. 다음 토큰은 ""에서 시작하여 ","로 끝납니다. 여기서 "This"를 출력으로 얻습니다. 마찬가지로 나머지 문자열은 공백에서 공백으로 토큰으로 분할되고 마지막으로 "."에서 마지막 토큰이 끝납니다.


strtok 런타임 함수는 다음과 같이 작동합니다.

strtok를 처음 호출 할 때 토큰화할 문자열을 제공합니다.

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

위의 문자열 공간에서 단어 사이의 좋은 구분 기호로 보이므로 다음을 사용하십시오.

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

지금 일어나는 일은 공백 문자가 발견 될 때까지 's'가 검색되고 첫 번째 토큰이 반환되고 ( 'this') p가 해당 토큰 (문자열)을 가리 킵니다.

다음 토큰을 얻고 동일한 문자열을 계속하려면 strtok가 이전에 전달 된 문자열에 대한 정적 포인터유지하므로 NULL이 첫 번째 인수로 전달됩니다 .

p = strtok(NULL," ");

p는 이제 'is'를 가리 킵니다.

그리고 더 이상 공백을 찾을 수 없을 때까지 마지막 문자열이 마지막 토큰 '문자열'로 반환됩니다.

더 편리하게 다음과 같이 작성하여 모든 토큰을 인쇄 할 수 있습니다.

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

편집하다:

반환 된 값을 저장하려면 strtok토큰을 다른 버퍼에 복사해야합니다. 예를 들어 strdup(p);원래 문자열 (내부 정적 포인터가 가리키는 strtok)이 토큰을 반환하기 위해 반복 사이에 수정되기 때문입니다.


strtok문자열에서 사용 가능한 다음 토큰을 가리키는 정적 내부 참조를 유지합니다. NULL 포인터를 전달하면 내부 참조에서 작동합니다.

이것이 strtok재진입이 아닌 이유 입니다. 새 포인터를 전달하자마자 이전 내부 참조가 손상됩니다.


strtok매개 변수 자체를 변경하지 않습니다 ( str). 해당 포인터를 로컬 정적 변수에 저장합니다. 그런 다음 매개 변수를 다시 전달하지 않고도 후속 호출에서 해당 매개 변수가 가리키는 내용을 변경할 수 있습니다 . (그리고 그것은 자신이 가지고있는 포인터를 전진시킬 수 있지만 작업을 수행해야합니다.)

POSIX strtok페이지에서 :

이 함수는 정적 저장소를 사용하여 호출 사이의 현재 문자열 위치를 추적합니다.

strtok_r이러한 유형의 마법을 수행하지 않는 스레드로부터 안전한 변형 ( )이 있습니다.


처음 호출 할 때 토큰화할 문자열을 strtok. 그런 다음 다음 토큰을 얻으려면 NULLNULL포인터를 반환하는 한 해당 함수에 제공 하면 됩니다.

strtok함수는 호출 할 때 처음 제공 한 문자열을 기록합니다. (다중 스레드 응용 프로그램에서는 정말 위험합니다)


strtok는 문자열을 토큰 화합니다. 즉 일련의 하위 문자열로 변환합니다.

이러한 토큰 (또는 하위 문자열)을 구분하는 구분 기호를 검색하여이를 수행합니다. 그리고 구분자를 지정합니다. 귀하의 경우에는 ''또는 ','또는 '.'를 원합니다. 또는 '-'는 구분 기호입니다.

이러한 토큰을 추출하는 프로그래밍 모델은 기본 문자열과 구분 기호 집합을 strtok하는 것입니다. 그런 다음 반복적으로 호출하고 매번 strtok가 찾은 다음 토큰을 반환합니다. null을 반환 할 때 메인 문자열의 끝에 도달 할 때까지. 또 다른 규칙은 처음에만 문자열을 전달하고 이후에는 NULL을 전달하는 것입니다. 이것은 새 문자열로 토큰 화의 새 세션을 시작하거나 이전 토큰 화 세션에서 토큰을 검색하는 경우 strtok에 알리는 방법입니다. strtok는 토큰 화 세션의 상태를 기억합니다. 이러한 이유로 재진입 또는 스레드로부터 안전하지 않습니다 (대신 strtok_r을 사용해야합니다). 알아야 할 또 다른 사항은 실제로 원래 문자열을 수정한다는 것입니다. 찾은 구분 기호에 대해 '\ 0'을 씁니다.

strtok를 간결하게 호출하는 한 가지 방법은 다음과 같습니다.

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

strtok는 입력 문자열을 수정합니다. 원래 문자열의 비트를 토큰으로 반환하도록 널 문자 ( '\ 0')를 그 안에 배치합니다. 사실 strtok는 메모리를 할당하지 않습니다. 일련의 상자로 문자열을 그리면 더 잘 이해할 수 있습니다.


strtok()작동 방식 을 이해하려면 먼저 정적 변수무엇인지 알아야합니다 . 이 링크 는 잘 설명합니다 ....

의 작업의 핵심 strtok()은 seccessive 호출 사이의 마지막 구분자의 위치를 ​​보존하는 것입니다 (이것이 연속 호출로 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이라는 Github 에서 유지 관리하는 문자열 처리 라이브러리에서 가져온 것입니다. 코드를 보거나 기여하십시오 :) https://github.com/fnoyanisi/zString


strtok는 두 번째 인수의 문자를 NULL로 바꾸고 NULL 문자도 문자열의 끝입니다.

http://www.cplusplus.com/reference/clibrary/cstring/strtok/


다음은 구분 기호에 해시 테이블을 사용하는 구현입니다. 즉, O (n ^ 2) 대신 O (n)을 의미합니다 (여기에 코드에 대한 링크가 있습니다) .

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

This is how i implemented strtok, Not that great but after working 2 hr on it finally got it worked. It does support multiple delimiters.

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

strtok() stores the pointer in static variable where did you last time left off , so on its 2nd call , when we pass the null , strtok() gets the pointer from the static variable .

If you provide the same string name , it again starts from beginning.

Moreover strtok() is destructive i.e. it make changes to the orignal string. so make sure you always have a copy of orignal one.

One more problem of using strtok() is that as it stores the address in static variables , in multithreaded programming calling strtok() more than once will cause an error. For this use strtok_r().


For those who are still having hard time understanding this strtok() function, take a look at this pythontutor example, it is a great tool to visualize your C (or C++, Python ...) code.

In case the link got broken, paste in:

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

Credits go to Anders K.

참고URL : https://stackoverflow.com/questions/3889992/how-does-strtok-split-the-string-into-tokens-in-c

반응형