web-dev-qa-db-fra.com

Comment fonctionne le strtok en C?

J'ai trouvé cet exemple de programme qui explique la fonction strtok:

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

Cependant, je ne vois pas comment cela peut fonctionner.

Comment est-il possible que pch = strtok (NULL, " ,.-"); renvoie un nouveau jeton. Je veux dire, nous appelons strtokwith NULL. Cela n'a pas beaucoup de sens pour moi.

20
user2426316

Deux choses à savoir sur strtok. Comme cela a été mentionné, il "maintient l'état interne". En outre, il gâche la chaîne que vous l'alimentez. Essentiellement, il écrira un '\0' où il trouve le jeton que vous avez fourni et renvoie un pointeur au début de la chaîne. En interne, il conserve l'emplacement du dernier jeton; et la prochaine fois que vous l'appelez, cela commence à partir de là.

Le corollaire important est que vous ne pouvez pas utiliser strtok sur un const char* "hello world"; type de chaîne, car vous obtiendrez une violation d'accès lorsque vous modifierez le contenu d'un const char* chaîne.

La "bonne" chose à propos de strtok est qu'elle ne copie pas réellement les chaînes - vous n'avez donc pas besoin de gérer l'allocation de mémoire supplémentaire, etc. Mais à moins de comprendre ce qui précède, vous aurez du mal à l'utiliser correctement.

Exemple - si vous avez "this, is, a, string", les appels successifs à strtok généreront des pointeurs comme suit (le ^ est la valeur renvoyée). Notez que le '\0' est ajouté là où se trouvent les jetons; cela signifie que la chaîne source est modifiée:

t  h  i  s  ,  i  s  ,  a  ,  s  t  r  i  n  g \0         this,is,a,string

t  h  i  s  \0 i  s  ,  a  ,  s  t  r  i  n  g \0         this
^
t  h  i  s  \0 i  s  \0 a  ,  s  t  r  i  n  g \0         is
               ^
t  h  i  s  \0 i  s  \0 a  \0 s  t  r  i  n  g \0         a
                        ^
t  h  i  s  \0 i  s  \0 a  \0 s  t  r  i  n  g \0         string
                              ^

J'espère que cela a du sens.

37
Floris

strtok maintient l'état interne. Lorsque vous l'appelez avec une valeur non NULL, il se réinitialise pour utiliser la chaîne que vous fournissez. Lorsque vous l'appelez avec NULL, il utilise cette chaîne et tout autre état doit actuellement renvoyer le jeton suivant.

En raison de la façon dont strtok fonctionne, vous devez vous assurer que vous établissez une liaison avec une version multithread du runtime C si vous écrivez une application multithread. Cela garantira que chaque thread obtiendra son propre état interne pour strtok.

2
Sean

La fonction strtok() stocke les données entre les appels. Il utilise ces données lorsque vous l'appelez avec un pointeur NULL.

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

Le point où le dernier jeton a été trouvé est conservé en interne par la fonction à utiliser lors du prochain appel (des implémentations de bibliothèque particulières ne sont pas requises pour éviter les courses de données).

2
Andy Thomas

La fonction strtok stocke les données dans une variable statique interne qui est partagée entre tous les threads.

Pour la sécurité des fils, vous devez utiliser strtok_r

De http://www.opensource.Apple.com/source/Libc/Libc-167/string.subproj/strtok.c

Jetez un oeil à static char *last;

char *
strtok(s, delim)
    register char *s;
    register const char *delim;
{
    register char *spanp;
    register int c, sc;
    char *tok;
    static char *last;


    if (s == NULL && (s = last) == NULL)
        return (NULL);

    /*
     * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
     */
cont:
    c = *s++;
    for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
        if (c == sc)
            goto cont;
    }

    if (c == 0) {       /* no non-delimiter characters */
        last = NULL;
        return (NULL);
    }
    tok = s - 1;

    /*
     * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
     * Note that delim must have one NUL; we stop if we see that, too.
     */
    for (;;) {
        c = *s++;
        spanp = (char *)delim;
        do {
            if ((sc = *spanp++) == c) {
                if (c == 0)
                    s = NULL;
                else
                    s[-1] = 0;
                last = s;
                return (tok);
            }
        } while (sc != 0);
    }
    /* NOTREACHED */
}
0
Juan Ramirez