2011-01-14 5 views
4

Considérons l'extrait de code suivant qui utilise strtok pour diviser la chaîne madddy.Essayer de comprendre strtok

char* str = (char*) malloc(sizeof("Madddy")); 
strcpy(str,"Madddy"); 

char* tmp = strtok(str,"d"); 
std::cout<<tmp; 

do 
{ 
    std::cout<<tmp; 
    tmp=strtok(NULL, "dddy"); 
}while(tmp!=NULL); 

Cela fonctionne très bien, la sortie est Ma. Mais en modifiant le strtok à ce qui suit,

tmp=strtok(NULL, "ay"); 

La sortie devient Madd. Alors, comment strtok fonctionne-t-il exactement? J'ai cette question parce que je m'attendais à ce que strtok prenne comme délimiteur tous les caractères qui se trouvent dans la chaîne de délimitation. Mais dans certains cas, c'est comme ça, mais dans quelques cas, cela donne des résultats inattendus. Quelqu'un pourrait-il m'aider à comprendre cela?

+6

Je pense honnêtement la bonne façon de le faire est d'arrêter complètement en utilisant 'strtok'. C'est une fonction difficile à utiliser, difficile à déboguer sans aucune garantie de sécurité. Il vaut probablement mieux utiliser une combinaison de 'string :: find' et' string :: substr' pour faire l'analyse. – templatetypedef

+0

Ou 'boost :: token_iterator' –

+0

Je suis prêt à répéter cela pour l'importance et l'emphase, d'autant plus que vous utilisez C++ et non C. Aussi, vous voudrez peut-être regarder dans boost :: tokenize. –

Répondre

1

Il semble que vous oubliez que vous avez appelé strtok la première fois (hors de la boucle) par le délimiteur "d".

Le strtok fonctionne bien.Vous devriez avoir une référence here.

Pour le second exemple (strtok("ay")):

D'abord, vous appelez strtok (str, "d"). Il cherchera le premier "d", et séparera votre chaîne. Plus précisément, il définit tmp = "Ma", et str = "ddy" (en supprimant le premier "d").

Ensuite, vous appelez strtok (str, "ay"). Il cherchera un "a" dans str, mais puisque votre chaîne est maintenant seulement "ddy", aucune correspondance ne se produit. Ensuite, il va chercher un "y". Donc str = "dd" et tmp = "".

Il imprime "Madd" comme vous l'avez vu.

+0

@Karthick: Le premier exemple fonctionne, mais il peut ne pas fonctionner comme vous le pensez. Je recommande que, au lieu de cout << tmp, en utilisant cout << tmp << "-" pour voir ce qui se passe réellement. Vous pouvez voir qu'il y a beaucoup de chaînes vides. –

10

"Essayer de comprendre strtok" Bonne chance!

Quoi qu'il en soit, nous sommes en 2011. Tokenise correctement:

std::string str("abc:def"); 
char split_char = ':'; 
std::istringstream split(str); 
std::vector<std::string> token; 

for (std::string each; std::getline(split, each, split_char); token.push_back(each)); 

: D

+3

Oui, je réalise que cela ne répond pas strictement à la question. Mais OMI est une alternative digne et supérieure, donc je pense que ça vaut toujours une réponse. –

+0

Je suis complètement conscient de ce problème .. Mais alors j'étais juste curieux de connaître les spécifications standard! –

3

Fred Flintstone probablement utilisé strtok(). Il précède les environnements multi-threads et bat (modifie) la chaîne source.

Appelé avec NULL pour le premier paramètre, il poursuit l'analyse de la dernière chaîne. Cette fonctionnalité était pratique, mais un peu inhabituel même en son jour.

+0

+1 pour la référence Fred Flintstone. –

2

En fait, votre code est erroné, pas étonnant que vous obtenez des résultats inattendus:

char* str = (char*) malloc(sizeof("Madddy")); 

devrait être

char* str = (char*) malloc(strlen("Madddy") + 1); 
+1

Oui, le premier exemple alloue probablement 4 octets (dans les environnements 32 bits), ce qui correspond à la taille d'un pointeur. Le type d'une constante chaîne comme '' abcdefghijkm "' est un pointeur (en particulier 'char *' ou 'const char *' selon le compilateur). – wallyk

+1

@wallyk: En fait, [ce n'est pas le cas] (http://codepad.org/H7zJkjCN). Le type de chaîne littérale est un tableau char. @Anders: Bien que le code soit bizarre, le vôtre fonctionne * identiquement. * –

+0

@Fred Nurk: Oh oui. Cela semble rompre le schéma, probablement pour être plus utile. C'est longtemps une construction que j'évite. (Excuses pour la mauvaise information deux commentaires ci-dessus. – wallyk

0

j'ai posé une question inspirée d'une autre question sur functions causing security problems/bad practise functions and the c standard library.

Pour citer la réponse qui m'a été donnée de là:

Un écueil commun avec la fonction strtok() est de supposer que la chaîne analysable reste inchangé, alors qu'il remplace en fait le caractère de séparation avec '\0'.

En outre, strtok() est utilisé en faisant appels ultérieurs, jusqu'à ce que la chaîne entière soit segmentée. Certains statut interne de mise en oeuvre de la bibliothèque stocker strtok() dans une variable globale , ce qui peut induire des suprises désagréables, si strtok() est appelé de plusieurs threads en même temps .

Comme vous avez étiqueté votre question C++, utilisez autre chose! Si vous voulez utiliser C, je suggère de mettre en place votre propre tokenizer qui fonctionne de manière sûre.

0

Depuis que vous avez changé votre tag pour être C et non pas C++, j'ai réécrit votre fonction pour utiliser printf afin que vous puissiez voir ce qui se passe. Hoang est correct. Vous voyez une sortie correcte, mais je pense que vous imprimez tout sur la même ligne, donc vous êtes confus par la sortie. Regardez la réponse de Hoang alors qu'il explique ce qui se passe correctement. En outre, comme d'autres l'ont remarqué, strtok détruit la chaîne d'entrée, donc vous devez faire attention à cela - et ce n'est pas sûr pour les threads. Mais si vous avez besoin d'un tokenizer rapide, cela fonctionne. Aussi, j'ai changé le code pour utiliser correctement strlen, et pas sizeof comme correctement indiqué par Anders.

Voici votre code modifié pour être plus C comme:

char* str = (char*) malloc(strlen("Madddy") + 1); 
strcpy(str,"Madddy"); 

char* tmp = strtok(str,"d"); 
printf ("first token: %s\n", tmp); 

do 
{ 
    tmp=strtok(NULL, "ay"); 
    if (tmp != NULL) { 
     printf ("next token: %s\n", tmp); 
    } 
} while(tmp != NULL);