web-dev-qa-db-fra.com

Tokenisation des chaînes en C

J'ai essayé de symboliser une chaîne en utilisant SPACE comme délimiteur, mais cela ne fonctionne pas. Quelqu'un at-il une suggestion sur pourquoi cela ne fonctionne pas?

Modifier: tokeniser en utilisant:

strtok(string, " ");

Le code est le suivant

pch = strtok (str," ");
while (pch != NULL)
{
  printf ("%s\n",pch);
  pch = strtok (NULL, " ");
}
24
kombo

Fais-le comme ça:

char s[256];
strcpy(s, "one two three");
char* token = strtok(s, " ");
while (token) {
    printf("token: %s\n", token);
    token = strtok(NULL, " ");
}

Remarque: strtok modifie la chaîne de sa tokenisation, elle ne peut donc pas être un const char*.

41
gbjbaanb

Voici un exemple d'utilisation de strtok, gardez à l'esprit que strtok est destructeur de sa chaîne d'entrée (et ne peut donc pas jamais être utilisé sur une constante de chaîne

char *p = strtok(str, " ");
while(p != NULL) {
    printf("%s\n", p);
    p = strtok(NULL, " ");
}

Fondamentalement, la chose à noter est que le fait de passer un NULL comme premier paramètre à strtok lui indique d'obtenir le prochain jeton de la chaîne qu'il était auparavant en train de tokeniser.

35
Evan Teran

strtok peut être très dangereux. Ce n'est pas sûr pour les threads. Son utilisation prévue doit être appelée à plusieurs reprises dans une boucle, en transmettant la sortie de l'appel précédent. La fonction strtok a une variable interne qui stocke l'état de l'appel strtok. Cet état n'est pas unique à chaque thread - il est global. Si un autre code utilise strtok dans un autre thread, vous rencontrez des problèmes. Pas le genre de problèmes que vous souhaitez rechercher non plus!

Je recommanderais de rechercher une implémentation regex ou d'utiliser sscanf pour séparer la chaîne.

Essaye ça:

char strprint[256];
char text[256];
strcpy(text, "My string to test");
while ( sscanf( text, "%s %s", strprint, text) > 0 ) {
   printf("token: %s\n", strprint);
}

Remarque: La chaîne "texte" est détruite lorsqu'elle est séparée. Ce n'est peut-être pas le comportement préféré =)

7
Kieveli

Vous pouvez simplifier le code en introduisant une variable supplémentaire.

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

int main()
{
    char str[100], *s = str, *t = NULL;

    strcpy(str, "a space delimited string");
    while ((t = strtok(s, " ")) != NULL) {
        s = NULL;
        printf(":%s:\n", t);
    }
    return 0;
}
3
Ferruccio

J'ai fait quelques fonctions de chaîne afin de diviser les valeurs, en utilisant moins de pointeurs que possible car ce code est destiné à fonctionner sur les processeurs PIC18F. Ces processeurs ne gèrent pas vraiment bien les pointeurs lorsque vous en avez peu RAM disponible:

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

char POSTREQ[255] = "pwd=123456&apply=Apply&d1=88&d2=100&pwr=1&mpx=Internal&stmo=Stereo&proc=Processor&cmp=Compressor&ip1=192&ip2=168&ip3=10&ip4=131&gw1=192&gw2=168&gw3=10&gw4=192&pt=80&lic=&A=A";

int findchar(char *string, int Start, char C) {
    while((string[Start] != 0)) { Start++; if(string[Start] == C) return Start; }
    return -1;
}

int findcharn(char *string, int Times, char C) {
   int i = 0, pos = 0, fnd = 0;

    while(i < Times) {
       fnd = findchar(string, pos, C);
        if(fnd < 0) return -1;
        if(fnd > 0) pos = fnd;
       i++;
   }
   return fnd;
}

void mid(char *in, char *out, int start, int end) {
    int i = 0;
    int size = end - start;

    for(i = 0; i < size; i++){
        out[i] = in[start + i + 1];
    }
    out[size] = 0;
}

void getvalue(char *out, int index) {
    mid(POSTREQ, out, findcharn(POSTREQ, index, '='), (findcharn(POSTREQ, index, '&') - 1));
}

void main() {
   char n_pwd[7];
   char n_d1[7];

   getvalue(n_d1, 1);

   printf("Value: %s\n", n_d1);
} 
2
Fernando

Voici une autre implémentation de strtok(), qui a la capacité de reconnaître les délimiteurs consécutifs (la strtok() de la bibliothèque standard ne l'a pas)

La fonction fait partie de la bibliothèque de chaînes sous licence BSD, appelée zString . Vous êtes plus que bienvenus pour contribuer :)

https://github.com/fnoyanisi/zString

char *zstring_strtok(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;
}

Comme mentionné dans les articles précédents, puisque strtok(), ou celle que j'ai implémentée ci-dessus, s'appuie sur une variable static *char Pour conserver l'emplacement du dernier délimiteur entre les appels consécutifs, une attention particulière doit être prise lors de la gestion avec des applications multi-thread.

0
fnisi

Lors de la lecture de la documentation de strtok, je vois que vous devez passer un pointeur NULL après le premier appel "d'initialisation". Peut-être que vous ne l'avez pas fait. Juste une supposition bien sûr.

0
xtofl