Como o strtok () divide a string em tokens em C?

Por favor, me explique o funcionamento da function strtok() . O manual diz que ela quebra a string em tokens. Eu não consigo entender pelo manual o que ele realmente faz.

Eu adicionei relógios em str e *pch para verificar o seu funcionamento, quando o primeiro loop while ocorreu, o conteúdo de str foram apenas “isso”. Como a saída mostrada abaixo foi impressa na canvas?

 /* strtok example */ #include  #include  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; } 

Saída:

 Splitting string "- isto, uma string de amostra."  em tokens:
 este
 uma
 amostra
 corda

    strtok() divide a string em tokens. Ou seja, a partir de qualquer um dos delimitadores para o próximo seria o seu token um. No seu caso, o token inicial será de “-” e terminará com o próximo espaço “”. Em seguida, o próximo token será iniciado a partir de “” e terminará com “,”. Aqui você obtém “This” como saída. Da mesma forma, o restante da string é dividido em tokens de espaço em espaço e, finalmente, termina o último token em “.”

    a function de tempo de execução strtok funciona assim

    a primeira vez que você chama strtok você fornece uma string que você deseja tokenize

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

    no espaço de string acima parece ser um bom delimitador entre palavras, então vamos usar isso:

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

    o que acontece agora é que ‘s’ é pesquisado até que o caractere de espaço seja encontrado, o primeiro token seja retornado (‘this’) e p aponta para esse token (string)

    para obter o próximo token e continuar com a mesma string, NULL é passado como primeiro argumento, pois strtok mantém um ponteiro estático para sua string passada anterior:

     p = strtok(NULL," "); 

    p agora aponta para ‘is’

    e assim por diante, até que não mais espaços possam ser encontrados, a última string é retornada como o último token ‘string’.

    mais convenientemente você poderia escrevê-lo assim para imprimir todos os tokens:

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

    EDITAR:

    Se você quiser armazenar os valores retornados de strtok você precisa copiar o token para outro buffer, por exemplo, strdup(p); já que a string original (apontada pelo ponteiro estático dentro de strtok ) é modificada entre as iterações para retornar o token.

    strtok mantém uma referência interna estática apontando para o próximo token disponível na string; Se você passar um ponteiro NULL, ele funcionará a partir dessa referência interna.

    Esta é a razão pela qual o strtok não é reentrante; Assim que você passar um novo ponteiro, essa referência interna antiga será derrotada.

    strtok não altera o parâmetro em si ( str ). Ele armazena esse ponteiro (em uma variável estática local). Em seguida, ele pode alterar para o qual esse parâmetro aponta nas chamadas subseqüentes sem ter o parâmetro passado de volta. (E pode avançar esse ponteiro que ele manteve, no entanto, ele precisa executar suas operações.)

    Da página de strtok do POSIX:

    Essa function usa armazenamento estático para acompanhar a posição atual da cadeia entre as chamadas.

    Existe uma variante thread-safe ( strtok_r ) que não faz esse tipo de mágica.

    A primeira vez que você chama, você fornece a string para tokenize para strtok . E então, para obter os seguintes tokens, você apenas dá NULL a essa function, contanto que ela retorne um ponteiro não NULL .

    A function strtok registra a string que você forneceu primeiro quando a chama. (O que é realmente perigoso para aplicativos multi-thread)

    strtok irá tokenize uma string, ou seja, convertê-lo em uma série de substrings.

    Ele faz isso procurando por delimitadores que separam esses tokens (ou substrings). E você especifica os delimitadores. No seu caso, você quer ” ou ‘,’ ou ‘.’ ou ‘-‘ para ser o delimitador.

    O modelo de programação para extrair esses tokens é que você entrega strtok à sua string principal e ao conjunto de delimitadores. Então você chama repetidamente, e cada vez que strtok retornará o próximo token que encontrar. Até atingir o final da string principal, quando retornar um nulo. Outra regra é que você passa a string somente na primeira vez e NULL para os tempos subseqüentes. Essa é uma maneira de informar strtok se você estiver iniciando uma nova session de tokenizing com uma nova string, ou se estiver recuperando tokens de uma session de tokenização anterior. Observe que strtok lembra seu estado para a session de tokenização. E por esse motivo não é reentrante ou thread safe (você deve estar usando strtok_r em vez disso). Outra coisa a saber é que ele realmente modifica a string original. Escreve ‘\ 0’ para os delimitadores que encontra.

    Uma maneira de invocar strtok, sucintamente, é a seguinte:

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

    Resultado:

     this is the string I want to parse 

    strtok modifica sua string de input. Ele coloca caracteres nulos (‘\ 0’) nele para que ele retorne bits da string original como tokens. Na verdade, strtok não aloca memory. Você pode entender melhor se desenhar a string como uma sequência de checkboxs.

    Para entender como o strtok() funciona, primeiro é necessário saber o que é uma variável estática . Este link explica muito bem ….

    A chave para a operação de strtok() é preservar a localização do último separador entre as chamadas seccessivas (é por isso que strtok() continua a analisar a cadeia muito original que é passada para ele quando é invocada com um null pointer em chamadas sucessivas) ..

    Dê uma olhada na minha própria implementação de strtok() , chamada zStrtok() , que possui uma funcionalidade sligtly diferente da fornecida por 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 

    E aqui está um exemplo de uso

      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) 

    O código é de uma biblioteca de processamento de strings que mantenho no Github , chamada zString. Dê uma olhada no código ou até mesmo contribua 🙂 https://github.com/fnoyanisi/zString

    strtok substitui os caracteres no segundo argumento por um caractere NULL e um caractere NULL também é o final de uma string.

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

    Aqui está minha implementação que usa a tabela de hash para o delimitador, o que significa que é O (n) em vez de O (n ^ 2) (aqui está um link para o código) :

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

    strtok () armazena o ponteiro na variável estática da última vez que você parou, então em sua segunda chamada, quando passamos o null, o strtok () pega o ponteiro da variável estática.

    Se você fornecer o mesmo nome de string, ele começará novamente do começo.

    Além disso, o strtok () é destrutivo, ou seja, faz alterações na sequência orignal. por isso, certifique-se sempre de ter uma cópia do original.

    Um outro problema do uso de strtok () é que, como ele armazena o endereço em variables ​​estáticas, na programação multithread, chamar strtok () mais de uma vez causará um erro. Para isso, use strtok_r ().

    Isto é como eu implementei strtok, Não que ótimo, mas depois de trabalhar 2 horas sobre ele finalmente conseguiu funcionou. Suporta vários delimitadores.

     #include "stdafx.h" #include  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; } 

    Para aqueles que ainda estão tendo dificuldade em entender essa function strtok() , dê uma olhada neste exemplo de python , é uma ótima ferramenta para visualizar seu código C (ou C ++, Python …).

    Caso o link seja quebrado, cole:

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

    Créditos vão para Anders K.