OVH Cloud OVH Cloud

fonction strtok

24 réponses
Avatar
Joemanix
Quesion simpliste certes mais c le debut.

Est il possible d'allouer le resulta de strtok("Bonjour Monsieur"," ") a une
variable ou a un tableua
De cette facon je decompose ma phrase .
Si oui comment
svp

10 réponses

1 2 3
Avatar
Emmanuel Delahaye
Joemanix wrote on 21/10/04 :
Quesion simpliste certes mais c le debut.

Est il possible d'allouer le resulta de strtok("Bonjour Monsieur"," ") a


Non, car la chaine doit être modifiable (tableau de char).

une
variable ou a un tableua
De cette facon je decompose ma phrase .
Si oui comment
svp


Exemple tiré de Borland C 3.1:

#include <string.h>
#include <stdio.h>

int main(void)
{
char input[16] = "abc,d";
char *p;

/* strtok places a NULL terminator
in front of the token, if found */
p = strtok(input, ",");
if (p) printf("%sn", p);

/* A second call to strtok using a NULL
as the first parameter returns a pointer
to the character following the token */
p = strtok(NULL, ",");
if (p) printf("%sn", p);
return 0;
}

Ceci-dit, strtok() utilisant une variable interne statique (pour
stocker la valeur courante du pointeur), elle est non-reentrante, et
par conséquend introduit des précautions d'emploi qui peuvent conduire
à ne pas pouvoir l'utiliser dans certains cas (appels imbriqués,
récursion, multitâche préemptif...)

Il existe une alternative Posix : strtok_r(). (r comme reentrant)

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"

Avatar
Joemanix
Thanks
"Emmanuel Delahaye" wrote in message
news:
Joemanix wrote on 21/10/04 :
Quesion simpliste certes mais c le debut.

Est il possible d'allouer le resulta de strtok("Bonjour Monsieur"," ") a


Non, car la chaine doit être modifiable (tableau de char).

une
variable ou a un tableua
De cette facon je decompose ma phrase .
Si oui comment
svp


Exemple tiré de Borland C 3.1:

#include <string.h>
#include <stdio.h>

int main(void)
{
char input[16] = "abc,d";
char *p;

/* strtok places a NULL terminator
in front of the token, if found */
p = strtok(input, ",");
if (p) printf("%sn", p);

/* A second call to strtok using a NULL
as the first parameter returns a pointer
to the character following the token */
p = strtok(NULL, ",");
if (p) printf("%sn", p);
return 0;
}

Ceci-dit, strtok() utilisant une variable interne statique (pour
stocker la valeur courante du pointeur), elle est non-reentrante, et
par conséquend introduit des précautions d'emploi qui peuvent conduire
à ne pas pouvoir l'utiliser dans certains cas (appels imbriqués,
récursion, multitâche préemptif...)

Il existe une alternative Posix : strtok_r(). (r comme reentrant)

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"




Avatar
Charlie Gordon
Bonjour,

Juste quelques remarques concertant l'infâme strtok

int main(void)
{
char input[16] = "abc,d";
char *p;

/* strtok places a NULL terminator
in front of the token, if found */
p = strtok(input, ",");
if (p) printf("%sn", p);

/* A second call to strtok using a NULL
as the first parameter returns a pointer
to the character following the token */
p = strtok(NULL, ",");
if (p) printf("%sn", p);
return 0;
}



Sous un jour innocent, ces quelques lignes de code recèlent un effet de bord
subtil qui peut causer des bugs introuvables.

Ceci-dit, strtok() utilisant une variable interne statique (pour
stocker la valeur courante du pointeur), elle est non-reentrante, et
par conséquend introduit des précautions d'emploi qui peuvent conduire
à ne pas pouvoir l'utiliser dans certains cas (appels imbriqués,
récursion, multitâche préemptif...)
Il existe une alternative Posix : strtok_r(). (r comme reentrant)



Exact ! En fait il ne faudrait pas l'utiliser du tout !

strtok a deux effets de bord : il modifie le tableau de char passé en argument,
ce qui est une sémantique désastreuse, qui n'est pas sans conséquences quand cet
argument est une constante chaine de caractères. De plus il stocke un pointeur
dans une variable globale privée pour honorer les appels ultérieurs avec un
argument NULL. C'est immonde. Le comité C89 devait avoir fumé la moquette pour
accepter une telle ignominie alors que des alternatives existaient, je cite le
Rationale C99 :

"7.21.5.8 The strtok function
This function was included in C89 to provide a convenient solution to many
simple problems of
lexical analysis, such as scanning command line arguments.
The strsep function was proposed as an enhanced replacement for the strtok
function.
While this is a common extension, it is easy enough for a user to provide this
functionality, and it
is unclear that an implementor can do a substantially better job; so, there was
not sufficient
support for adding this feature."

Cette variable globale rend l'utilisation de strtok problématique : la non
réentrance n'est pas seulement un problème de concurrence (multi-threading)
elle interdit aussi, et c'est moins évident pour le commun des programmeurs,
l'utilisation imbriquée. Je donne un exemple simple, non récursif :

#include <stdio.h>
#include <string.h>

int main(void) {
/* analyse d'un fichier simple */
/* un enregistrement par ligne, champs séparés par des ';' */
/* but du programme : */
/* extraire les mots qui composent les champs du fichier */
char buf[1024];
char *field, *word;

while (fgets(buf, sizeof(buf), stdin) != NULL) {
field = strtok(buf, ";");
while (field != NULL) {
/* analyse d'un champ */
word = strtok(field, " ");
while (word != NULL) {
printf("%sn", word);
word = strtok(NULL, " ");
}
field = strtok(NULL, ";");
}
}
return 0;
}

Cette implémentation est incapable d'analyser correctement le fichier suivant :

Jean Dupont;Anne Durand

Le problème apparaît facilement à la lecture du code. Mais si nous avions
utilisé une fonction pour analyser un champ, avec la même implémentation,
l'utilisation imbriquée de strtok aurait été beaucoup plus difficile à voir.
D'ailleurs rien ne nous garantit que printf n'utilise pas strtok !

L'utilisation de strtok_r résout ce problème :

int main(void) {
/* analyse d'un fichier simple */
/* un enregistrement par ligne, champs séparés par des ';' */
/* but du programme : */
/* extraire les mots qui composent les champs du fichier */
char buf[1024];
char *field, *word;
char *field_end, *word_end;

while (fgets(buf, sizeof(buf), stdin) != NULL) {
field = strtok_r(buf, ";", &field_end);
while (field != NULL) {
/* analyse d'un champ */
word = strtok_r(field, " ", &word_end);
while (word != NULL) {
printf("%sn", word);
word = strtok_r(NULL, " ", &word_end);
}
field = strtok_r(NULL, ";", &field_end);
}
}
return 0;
}

En conclusion : il ne faut pas utiliser strtok, même pour des problèmes simples.
Utiliser strtok_r() qui est très souvent disponible (POSIX) ou l'implémenter
soi-meme (excellent exercice laissé au lecteur, que l'ont peut simplifier en se
limitant au cas très courant d'un séparateur unique)

Chqrlie.

PS: le bug subtil dans le programme initial est que le tableau passé en
paramètre à strtok est une variable automatique. strtok va donc stocker dans sa
variable interne dégeulasse un pointeur sur ce tableau éminement temporaire.
C'est très similaire au bug classique :

char *numtostr(int n) {
char buf[32];
sprintf(buf, "%d", n);
return buf;
}

Si, après le retour de la fonction dont le tableau était une variable locale, un
appel est fait à strtok avec NULL pour premier argument, comme c'est le cas dans
mon exemple d'utilisation imbriquée, ce pointeur sera utilisé non seulement pour
analyser un buffer disparu mais aussi pour le modifier dans le cas ou cette
analyse réussit. On modifie alors un octet quelque part dans la pile. Les
conséquences sont imprévisibles. Un bug très difficile à trouver, qui causera
des comportements erratiques, voire des plantages pas nécessairement
systématiques...

NEVER USE STRTOK !


Avatar
Emmanuel Delahaye
Charlie Gordon wrote on 22/10/04 :
NEVER USE STRTOK !


Quand je me permettais de donner ce genre d'avis, on me traitait
d'intégriste. Maintenant, je dis la même chose, mais sous une apparence
plus nuancée, ça passe mieux, apparament... On vit dans un monde
d'image et d'apparence, il faut faire avec...

Ceci-dit, strtok() utilisant une variable interne statique (pour
stocker la valeur courante du pointeur), elle est non-reentrante, et
par conséquend introduit des précautions d'emploi qui peuvent conduire
à ne pas pouvoir l'utiliser dans certains cas (appels imbriqués,
récursion, multitâche préemptif...)




--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"



Avatar
Marc Boyer
In article , Emmanuel Delahaye wrote:
Charlie Gordon wrote on 22/10/04 :
NEVER USE STRTOK !


Quand je me permettais de donner ce genre d'avis, on me traitait
d'intégriste. Maintenant, je dis la même chose, mais sous une apparence
plus nuancée, ça passe mieux, apparament... On vit dans un monde
d'image et d'apparence, il faut faire avec...


Il y a AMHA deux classes distinctes d'avis:
- l'avis argumenté, dont l'acceptation dépend de la qualité des
arguments ;
- l'avis non argumenté, dit aussi "argument d'autorité" dont
l'acceptation dépend du capital confiance de l'orateur
face au public.

Marc Boyer
--
Je ne respecte plus le code de la route à vélo depuis une double fracture
due au fait que j'étais le seul à le respecter.


Avatar
Pierre Maurette
"Joemanix" a écrit:

Quesion simpliste certes mais c le debut.

Est il possible d'allouer le resulta de strtok("Bonjour Monsieur"," ") a une
variable ou a un tableua
De cette facon je decompose ma phrase .
Si oui comment
char texte[] = "Je suis le ténébreux le veuf l'inconsolé";

char** tab;
int nb_mots = 1;
int curseur = 0;
int i = 0;

while(texte[curseur] != '')
nb_mots += (texte[curseur++] == ' ');

tab = (char**) malloc(nb_mots);

if((tab[i++] = strtok(texte, " ")) != NULL)
while((tab[i++] = strtok(NULL, " ")) != NULL);

for(curseur = 0; curseur < nb_mots; curseur++) puts(tab[curseur]);

[ça redonde pour le nombre de mots, et en plus ça doit être plein
d'erreurs, mais ça peut inspirer]
--
Pierre

Avatar
Charlie Gordon
Marc Boyer" wrote in message
news:claug6$gke$
In article , Emmanuel Delahaye
wrote:

Charlie Gordon wrote on 22/10/04 :
NEVER USE STRTOK !


Quand je me permettais de donner ce genre d'avis, on me traitait
d'intégriste. Maintenant, je dis la même chose, mais sous une apparence
plus nuancée, ça passe mieux, apparament... On vit dans un monde
d'image et d'apparence, il faut faire avec...


Il y a AMHA deux classes distinctes d'avis:
- l'avis argumenté, dont l'acceptation dépend de la qualité des
arguments ;
- l'avis non argumenté, dit aussi "argument d'autorité" dont
l'acceptation dépend du capital confiance de l'orateur
face au public.


Je suis d'accord, et comme je ne dispose pas d'un capital confiance illimité
auprès du public très varié de ce forum, j'ai argumenté ma position avec moult
détails et exemples. L'imprécation n'est que la conclusion logique de mon
exposé et les utilisateurs brimés de cette fonction piégée reconnaitront
peut-être la justesse de ce résultat.

Chqrlie.

PS: soyons clairs : NEVER USE STRNCPY EITHER, NOR STRNCAT, NOR GETS.



Avatar
Hamiral
PS: soyons clairs : NEVER USE STRNCPY EITHER, NOR STRNCAT, NOR GETS.


Je ne suis pas d'accord, pour strncpy on vérifie que la chaîne de
destination est assez grande, on la remplit de '' et on procède à la
copie. Pour strncat, on vérifie juste que la chaîne de destination est
assez grande, strncat mettant elle-même le '' terminal.
En bref il faut lire les manpages.

Et pour gets, je ne vous comprend pas ... Quoique je n'utilise que
fgets, mais c'est la même chose non ? gets(str) ne correspond-elle pas à
fgets(stdin, str) ? Ah oui il faut bien sûr que str soit suffisamment
allouée, mais bon si vous ne voulez pas faire votre travail de gestion
de la mémoire, n'utilisez donc aucune fonction de manipulation de chaîne ...
--
Florent Curé
florent (point) cure (arobase) free (point) fr

Avatar
Emmanuel Delahaye
Hamiral wrote on 24/10/04 :
Et pour gets, je ne vous comprend pas ... Quoique je n'utilise que fgets,
mais c'est la même chose non ? gets(str) ne correspond-elle pas à
fgets(stdin, str) ?


Non. Il manque l'information de taille qui existe avec fgets().

Ah oui il faut bien sûr que str soit suffisamment
allouée, mais bon si vous ne voulez pas faire votre travail de gestion de la
mémoire, n'utilisez donc aucune fonction de manipulation de chaîne ...


Le problème est que sur certaines machines, on ne peut pas utiliser
gets() de façon sûre.

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"

Avatar
James Kanze
"Charlie Gordon" writes:

|> PS: soyons clairs : NEVER USE STRNCPY EITHER, NOR STRNCAT, NOR GETS.

Ni sprintf non plus.

Mais soyons honnête. Il y a une différence quand même. Un programme qui
utilise gets ne peut jamais être correct. Période. Pour les autres,
c'est possible qu'il le soit aujourd'hui ; c'est simplement que ce n'est
pas possible de s'assurer qu'il le serait encore après la moindre
modification.

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
1 2 3