web-dev-qa-db-fra.com

Comment strtok () divise-t-il la chaîne en jetons en C?

Veuillez m'expliquer le fonctionnement de la fonction strtok(). Le manuel dit qu'il divise la chaîne en jetons. Je suis incapable de comprendre dans le manuel ce que cela fait réellement.

J'ai ajouté des surveillances sur str et *pch pour vérifier son fonctionnement. Lorsque la première boucle while s'est produite, le contenu de str n'était que "ceci". Comment la sortie présentée ci-dessous a-t-elle été imprimée à l'écran?

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

Sortie:

 Fractionner une chaîne "- Ceci, un exemple de chaîne." en jetons: 
 Ce 
 a 
 échantillon 
 chaîne 
87
Fahad Uddin

strtok() divise la chaîne en jetons. c’est-à-dire que l’on partirait d’un délimiteur au suivant serait votre seul jeton. Dans votre cas, le jeton de départ sera de "-" et se terminera par le prochain espace "". Ensuite, le jeton suivant commencera par "" et se terminera par ",". Ici, vous obtenez "This" en sortie. De la même manière, le reste de la chaîne est scindé en jetons d’espace en espace et termine le dernier jeton sur «.

35
Sachin Shanbhag

la fonction d'exécution strtok fonctionne comme ceci

la première fois que vous appelez strtok, vous fournissez une chaîne que vous souhaitez tokeniser

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

dans la chaîne de caractères ci-dessus, l’espace semble être un bon séparateur entre les mots, alors utilisons:

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

que se passe-t-il maintenant est que "s" est recherché jusqu'à ce que le caractère espace soit trouvé, le premier jeton est renvoyé ("ceci") et p pointe sur ce jeton (chaîne)

afin d'obtenir le prochain jeton et de continuer avec la même chaîne, NULL est passé en tant que premier argument .__, car strtok conserve un pointeur statique vers votre chaîne passée précédente:

p = strtok(NULL," ");

p pointe maintenant sur 'est'

et ainsi de suite jusqu'à ce qu'il ne reste plus d'espaces, la dernière chaîne est renvoyée en tant que dernier jeton 'chaîne'.

plus facilement, vous pourriez l'écrire comme ceci au lieu d'imprimer tous les jetons:

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

MODIFIER:

Si vous souhaitez stocker les valeurs renvoyées à partir de strtok, vous devez copier le jeton dans un autre tampon, par exemple. strdup(p); puisque la chaîne d'origine (indiquée par le pointeur statique à l'intérieur de strtok) est modifiée entre les itérations afin de renvoyer le jeton.

176
Anders

strtok conserve une référence interne statique qui pointe vers le prochain jeton disponible dans la chaîne; Si vous lui passez un pointeur NULL, il travaillera à partir de cette référence interne. 

C'est la raison pour laquelle strtok n'est pas ré-entrant; dès que vous passez un nouveau pointeur, cette ancienne référence interne est effacée. 

19
John Bode

strtok ne change pas le paramètre lui-même (str). Il stocke ce pointeur (dans une variable statique locale). Il peut ensuite changer ce que le paramètre désigne en lors des appels suivants sans que le paramètre ne soit renvoyé. (Et il peut faire avancer le pointeur qu’il a conservé, mais il a besoin d’effectuer ses opérations.)

De la page POSIX strtok :

Cette fonction utilise le stockage statique pour garder une trace de la position actuelle de la chaîne entre les appels.

Il existe une variante thread-safe (strtok_r) qui ne fait pas ce type de magie.

10
Mat

La première fois que vous l'appelez, vous fournissez la chaîne à tokenize pour strtok. Et puis, pour obtenir les jetons suivants, vous donnez simplement NULL à cette fonction, tant qu'elle renvoie un pointeur non NULL.

La fonction strtok enregistre la chaîne que vous avez fournie en premier lorsque vous l'appelez. (Ce qui est vraiment dangereux pour les applications multi-thread)

7
tibur

strtok va tokeniser une chaîne, c'est-à-dire la convertir en une série de sous-chaînes.

Pour ce faire, il recherche des délimiteurs séparant ces jetons (ou sous-chaînes). Et vous spécifiez les délimiteurs. Dans votre cas, vous voulez "ou", "ou". ou '-' pour être le délimiteur.

Le modèle de programmation permettant d'extraire ces jetons consiste à donner à strtok votre chaîne principale et l'ensemble des délimiteurs. Ensuite, vous l’appelez à plusieurs reprises et chaque fois, strtok retournera le prochain jeton qu’il trouvera. Jusqu'à ce qu'il atteigne la fin de la chaîne principale, lorsqu'il renvoie une valeur nulle. Une autre règle est que vous ne passez la chaîne que la première fois et NULL pour les temps suivants. C'est un moyen de dire à strtok si vous démarrez une nouvelle session de création de jetons avec une nouvelle chaîne ou si vous récupérez des jetons d'une session précédente. Notez que strtok se souvient de son état pour la session de création de jetons. Et pour cette raison, il n’est ni réentrant, ni thread-safe (vous devriez plutôt utiliser strtok_r). Une autre chose à savoir est que cela modifie réellement la chaîne d'origine. Il écrit '\ 0' pour les délimiteurs qu'il trouve.

Une façon d'invoquer strtok, succinctement, est la suivante:

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

Résultat:

this
is
the
string
I
want
to
parse
5
Ziffusion

strtok modifie sa chaîne d'entrée. Il y place des caractères nuls ("0") afin qu'il renvoie les bits de la chaîne d'origine sous forme de jetons. En fait, strtok n'alloue pas de mémoire. Vous comprendrez peut-être mieux si vous dessinez la chaîne comme une séquence de cases. 

4
xpmatteo

Pour comprendre le fonctionnement de strtok(), il faut d’abord savoir quelle est la variable statique . Ce lien explique assez bien ....

La clé du fonctionnement de strtok() est de conserver l’emplacement du dernier séparateur entre les appels secondaires (c’est pourquoi strtok() continue à analyser la chaîne très originale qui lui est transmise lorsqu’il est appelé avec un null pointer dans des appels successifs).

Jetez un coup d'œil à ma propre implémentation strtok(), appelée zStrtok(), qui a une fonctionnalité légèrement différente de celle fournie par 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;
}

Et voici un exemple d'utilisation

  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)

Le code provient de une bibliothèque de traitement de chaînes que je gère sur Github , appelée zString. Jetez un coup d'œil au code, ou contribuez même:) https://github.com/fnoyanisi/zString

3
Fehmi Noyan ISI

strtok remplace les caractères du deuxième argument par un NULL et un caractère NULL est également la fin d'une chaîne.

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

1
Patrick

Voici mon implémentation qui utilise une table de hachage pour le délimiteur, ce qui signifie qu'il O(n) au lieu de O (n ^ 2) (voici un lien vers le code) :

#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
Kohn1001

strtok () stocke le pointeur dans la variable statique où vous l'aviez laissée la dernière fois. Ainsi, lors de son deuxième appel, lorsque nous passons le null, strtok () obtient le pointeur de la variable statique.

Si vous fournissez le même nom de chaîne, cela recommence depuis le début.

De plus, strtok () est destructif, c’est-à-dire qu’il modifie la chaîne originale. alors assurez-vous de toujours avoir une copie originale.

Un autre problème lié à l'utilisation de strtok () est que, puisqu'il stocke l'adresse dans des variables statiques, la programmation multithread appelle plusieurs fois strtok (), ce qui provoque une erreur. Pour cela, utilisez strtok_r ().

1
Vaibhav

C’est comme ça que j’ai mis en place strtok, pas terrible mais après 2 heures de travail, il a finalement fonctionné. Il prend en charge plusieurs délimiteurs.

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

Pour ceux qui ont encore du mal à comprendre cette fonction strtok(), jetez un œil à cet exemple pythontutor , c’est un excellent outil pour visualiser votre code C (ou C++, Python ...).

Au cas où le lien serait brisé, collez:

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

Crédits vont à Anders K.

0
clifer4