Strtok (): Si les jetons sont délimités par des délimiteurs, pourquoi le dernier jeton entre un délimiteur et le null ‘\ 0’?

Dans le programme suivant, strtok() fonctionne comme prévu dans la majeure partie, mais je ne peux tout simplement pas comprendre la raison d’un constat. J’ai lu à propos de strtok() que:

Pour déterminer le début et la fin d’un jeton, la fonction balaie d’abord à partir de l’emplacement de départ le premier caractère non contenu dans les délimiteurs (qui devient le début du jeton). Puis, à partir de ce début du jeton, balaye le premier caractère contenu dans les délimiteurs, qui devient la fin du jeton.

Source: http://www.cplusplus.com/reference/cssortingng/strtok/

Et comme nous le soaps, strtok() place un \0 à la fin de chaque jeton. Mais dans le programme suivant, le dernier délimiteur est un point ( . ), Après quoi il y a un crapaud entre ce point et le guillemet ( " ). Maintenant, le point est un délimiteur dans mon programme, mais il n’y a pas de délimiteur après Toad , pas même un espace blanc (qui est un délimiteur dans mon programme) .Veuillez dissiper la confusion suivante qui découle de ce postulat:

Pourquoi strtok() considère-t-il le crapaud comme un jeton même s’il ne se trouve pas entre 2 délimiteurs? C’est ce que j’ai lu à propos de strtok() quand il rencontre un caractère NULL ( \0 ):

Une fois que le caractère nul final de str a été trouvé dans un appel à strtok, tous les appels suivants à cette fonction avec un pointeur nul en tant que premier argument renvoient un pointeur nul.

Source: http://www.cplusplus.com/reference/cssortingng/strtok/

Nulle part il ne dit qu’une fois qu’un caractère nul est rencontré, un pointeur vers le début du jeton est renvoyé (nous n’avons même pas de jeton ici car nous n’avons pas eu la fin du jeton car aucun caractère de délimiteur n’a été trouvé. après que l’parsing ait commencé au début du jeton (c’est-à-dire à partir de ‘T’ de Toad), nous n’avons trouvé qu’un caractère nul, pas un délimiteur ). Alors pourquoi la partie entre le dernier séparateur et le guillemet de la chaîne d’argument est-elle considérée comme un jeton par strtok() ? S’il vous plaît expliquer ceci.

Code:

 #include  #include  int main () { char str[] =" Falcon,eagle-hawk..;buzzard,gull..pigeon sparrow,hen;owl.Toad"; char * pch=strtok(str," ;,.-"); while (pch != NULL) { printf ("%s\n",pch); pch = strtok (NULL, " ;,.-"); } return 0; } 

Sortie:

Faucon
Aigle
faucon
buse
mouette
Pigeon
moineau
poule
hibou
Crapaud

La spécification standard de strtok (7.24.5.8) est assez claire. En particulier, le paragraphe 4 (soulignement ajouté par moi) est directement pertinent pour la question, si je comprends bien:

3 Le premier appel de la séquence recherche dans la chaîne pointée par s1 le premier caractère non contenu dans la chaîne de séparation actuelle pointée par s2 . Si aucun caractère de ce type n’est trouvé, la chaîne pointée par s1 ne contient aucun jeton et la fonction strtok renvoie un pointeur nul. Si un tel caractère est trouvé, c’est le début du premier jeton.

4 La fonction strtok recherche ensuite un caractère contenu dans la chaîne de séparation actuelle. Si aucun caractère de ce type n’est trouvé, le jeton en cours s’étend jusqu’à la fin de la chaîne pointée par s1 et les recherches ultérieures pour un jeton renverront un pointeur nul . Si un tel caractère est trouvé, il est remplacé par un caractère nul, qui termine le jeton actuel. La fonction strtok enregistre un pointeur sur le caractère suivant, à partir duquel la recherche suivante d’un jeton commence.

En appel

 char *where = strtok(ssortingng_or_NULL, delimiters); 

le jeton (un pointeur sur) renvoyé – le cas échéant – s’étend du premier caractère non-délimiteur trouvé de la position de départ (inclusif) jusqu’au prochain caractère délimiteur (exclusif), le cas échéant, ou à la fin de la chaîne, si aucun caractère de délimitation ultérieur n’existe.

La description liée ne mentionne pas explicitement le cas d’un jeton s’étendant jusqu’à la fin de la chaîne, contrairement à la norme, il est donc incomplet à cet égard.

Aller à la description dans POSIX pour strtok() , la description dit:

char *strtok(char *ressortingct s1, const char *ressortingct s2);

Une séquence d’appels à strtok() divise la chaîne pointée par s1 en une séquence de jetons, chacun d’eux étant délimité par un octet de la chaîne pointée par s2 . Le premier appel de la séquence a le premier argument s1 , suivi des appels avec un pointeur nul comme premier argument. La chaîne de séparation pointée par s2 peut différer d’un appel à l’autre.

Le premier appel de la séquence recherche dans la chaîne pointée par s1 le premier octet non contenu dans la chaîne de séparation actuelle pointée par s2 . Si aucun octet de ce type n’est trouvé, il n’y a pas de jetons dans la chaîne pointée par s1 et strtok() doit renvoyer un pointeur nul. Si un tel octet est trouvé, c’est le début du premier jeton.

La fonction strtok() recherche ensuite à partir de là un octet contenu dans la chaîne de séparation actuelle. Si aucun octet de ce type n’est trouvé, le jeton en cours s’étend jusqu’à la fin de la chaîne indiquée par s1 et les recherches ultérieures pour un jeton doivent renvoyer un pointeur nul. Si un tel octet est trouvé, il est écrasé par un caractère NUL, qui termine le jeton actuel. La fonction strtok() enregistre un pointeur sur l’octet suivant, à partir duquel la recherche suivante d’un jeton doit commencer.

Notez la deuxième phrase du troisième paragraphe:

Si aucun octet de ce type n’est trouvé, le jeton en cours s’étend jusqu’à la fin de la chaîne indiquée par s1 et les recherches ultérieures pour un jeton doivent renvoyer un pointeur nul.

Cela indique clairement que dans l’exemple de la question, Toad est en effet un gage. Une façon de penser à cela est que la liste des délimiteurs inclut toujours le NUL '\0' à la fin de la chaîne de délimiteur.


Ayant diagnostiqué cela, notez que strtok() n’est pas une bonne fonction à utiliser – ce n’est pas thread-safe ou réentrant. Sous Windows, vous pouvez utiliser strtok_s() place. sous Unix, vous pouvez généralement utiliser strtok_r() . Ce sont de meilleures fonctions car elles ne stockent pas en interne le pointeur sur lequel la recherche doit reprendre.

strtok() donné que strtok() n’est pas réentrant, vous ne pouvez pas appeler une fonction qui utilise strtok() depuis une fonction qui utilise elle-même strtok() lorsqu’elle utilise strtok() . De plus, toute fonction de bibliothèque qui utilise strtok() doit être clairement identifiée, car elle ne peut pas être appelée à partir d’une fonction utilisant strtok() . Donc, utiliser strtok() rend la vie difficile.

L’autre problème avec la famille de fonctions strtok() (et avec strsep() , qui est liée) est qu’elles écrasent le délimiteur; vous ne pouvez pas savoir ce qu’était le délimiteur après que le tokenizer a tokenisé la chaîne. Cela peut avoir de l’importance dans certaines applications (telles que l’parsing des lignes de commande shell; que le délimiteur soit un tuyau, un point-virgule ou un esperluette (ou …). Les parsingurs syntaxiques de shell n’utilisent donc généralement pas strtok() , malgré le nombre. de questions sur SO sur les shells pour lesquels l’parsingur utilise strtok() .

En règle générale, strtok_r() , et il vous appartient de décider si strtok_r() ou strtok_s() convient à vos besoins.

Parce que cplusplus.com ne vous raconte pas toute l’histoire. Cppreference.com a une meilleure description.

Cplusplus.com omet également de mentionner que strtok n’est pas thread-safe et ne documente que la fonction strtok du langage de programmation C ++, alors que cppreference.com mentionne le problème de la sécurité des threads et documente les fonctions strtok des programmations C et C ++ . langues.

Êtes-vous peut-être simplement en train de mal lire la description?

Une fois que le caractère nul final de str a été trouvé dans un appel à strtok, tous les appels suivants à cette fonction avec un pointeur nul en tant que premier argument renvoient un pointeur nul.

Étant donné ‘ultérieur’, je lis ceci comme tout appel à strtok après celui qui a découvert \0 , pas nécessairement le courant lui-même. La définition est donc cohérente avec le comportement (et avec ce que vous attendez de strtok ).

strtok coupe une chaîne en une séquence de jetons, séparés par les délimètres donnés. Les délimètres ne séparent que les jetons et ne les terminent pas nécessairement des deux côtés.