Comment puis-je écrire une fonction pour diviser et retourner un tableau pour une chaîne de caractères avec des délimiteurs dans le langage de programmation C?
char* str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
str_split(str,',');
Comment puis-je écrire une fonction pour diviser et retourner un tableau pour une chaîne de caractères avec des délimiteurs dans le langage de programmation C?
char* str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
str_split(str,',');
Vous pouvez utiliser la fonction strtok()
pour diviser une chaîne de caractères (et spécifier le délimiteur à utiliser). Notez que strtok()
modifiera la chaîne qui lui est passée. Si la chaîne d'origine est requise ailleurs, faites-en une copie et passez la copie à strtok()
.
ÉDIT:
Exemple (remarquez qu'il ne gère pas les délimiteurs consécutifs, par exemple "JAN,,,FEB,MAR"):
#include
#include
#include
#include
char** str_split(char* a_str, const char a_delim)
{
char** result = 0;
size_t count = 0;
char* tmp = a_str;
char* last_comma = 0;
char delim[2];
delim[0] = a_delim;
delim[1] = 0;
/* Comptez le nombre d'éléments pouvant être extraits. */
while (*tmp)
{
if (a_delim == *tmp)
{
count++;
last_comma = tmp;
}
tmp++;
}
/* Ajoutez de l'espace pour le jeton final. */
count += last_comma < (a_str + strlen(a_str) - 1);
/* Ajoutez de l'espace pour la chaîne nulle terminale afin que l'appelant sache où se termine la liste des chaînes retournées. */
count++;
result = malloc(sizeof(char*) * count);
if (result)
{
size_t idx = 0;
char* token = strtok(a_str, delim);
while (token)
{
assert(idx < count);
*(result + idx++) = strdup(token);
token = strtok(0, delim);
}
assert(idx == count - 1);
*(result + idx) = 0;
}
return result;
}
int main()
{
char months[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
char** tokens;
printf("months=[%s]\n\n", months);
tokens = str_split(months, ',');
if (tokens)
{
int i;
for (i = 0; *(tokens + i); i++)
{
printf("month=[%s]\n", *(tokens + i));
free(*(tokens + i));
}
printf("\n");
free(tokens);
}
return 0;
}
Sortie:
$ ./main.exe
months=[JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC]
month=[JAN]
month=[FEB]
month=[MAR]
month=[APR]
month=[MAY]
month=[JUN]
month=[JUL]
month=[AUG]
month=[SEP]
month=[OCT]
month=[NOV]
month=[DEC]
Salut. Je pense que la fonction a codé en dur "," comme séparateur : char* token = strtok(a_str, ",");
@SteveP, bien vu. Ce code est là depuis des mois et personne ne l'a remarqué. Je vais le corriger sous peu.
Comme il s'agit peut-être de la question/réponse canonique sur Stack Overflow à ce sujet, n'y a-t-il pas quelques limitations en ce qui concerne le multi-threading en utilisant strtok?
Je pense que strsep
est toujours le meilleur outil pour cela :
while ((token = strsep(&str, ","))) my_fn(token);
C'est littéralement une ligne qui divise une chaîne de caractères.
Les parenthèses supplémentaires sont un élément stylistique pour indiquer que nous testons intentionnellement le résultat d'une affectation, et non un opérateur d'égalité ==
.
Pour que ce modèle fonctionne, token
et str
ont tous deux le type char *
. Si vous avez commencé avec une chaîne de caractères littérale, vous voudriez d'abord en faire une copie :
// Modèle plus général :
const char *my_str_literal = "JAN,FEB,MAR";
char *token, *str, *tofree;
tofree = str = strdup(my_str_literal); // Nous possédons maintenant la mémoire de str.
while ((token = strsep(&str, ","))) my_fn(token);
free(tofree);
Si deux délimiteurs apparaissent ensemble dans str
, vous obtiendrez une valeur de token
qui est une chaîne vide. La valeur de str
est modifiée à chaque délimiteur rencontré, en écrivant par-dessus un octet nul - une autre bonne raison de copier d'abord la chaîne de caractères analysée.
Dans un commentaire, quelqu'un a suggéré que strtok
est mieux que strsep
parce que strtok
est plus portable. Ubuntu et Mac OS X ont strsep
; il est raisonnable de supposer que d'autres systèmes de type Unix en ont aussi. Windows ne dispose pas de strsep
, mais il possède strbrk
qui permet ce remplacement court et efficace de strsep
:
char *strsep(char **stringp, const char *delim) {
if (*stringp == NULL) { return NULL; }
char *token_start = *stringp;
*stringp = strpbrk(token_start, delim);
if (*stringp) {
**stringp = '\0';
(*stringp)++;
}
return token_start;
}
Voici une bonne explication de strsep
vs strtok
. Les avantages et les inconvénients peuvent être jugés subjectivement ; cependant, je pense que c'est un signe révélateur que strsep
a été conçu comme un remplacement de strtok
.
La méthode ci-dessous fera tout le travail (allocation de mémoire, comptage de la longueur) pour vous. Plus d'informations et la description peuvent être trouvées ici - Implémentation de la méthode Java String.split() pour diviser une chaîne C
int split (const char *str, char c, char ***arr)
{
int count = 1;
int token_len = 1;
int i = 0;
char *p;
char *t;
p = str;
while (*p != '\0')
{
if (*p == c)
count++;
p++;
}
*arr = (char**) malloc(sizeof(char*) * count);
if (*arr == NULL)
exit(1);
p = str;
while (*p != '\0')
{
if (*p == c)
{
(*arr)[i] = (char*) malloc( sizeof(char) * token_len );
if ((*arr)[i] == NULL)
exit(1);
token_len = 0;
i++;
}
p++;
token_len++;
}
(*arr)[i] = (char*) malloc( sizeof(char) * token_len );
if ((*arr)[i] == NULL)
exit(1);
i = 0;
p = str;
t = ((*arr)[i]);
while (*p != '\0')
{
if (*p != c && *p != '\0')
{
*t = *p;
t++;
}
else
{
*t = '\0';
i++;
t = ((*arr)[i]);
}
p++;
}
return count;
}
Comment l'utiliser:
int main (int argc, char ** argv)
{
int i;
char *s = "Bonjour, ceci est un module de test pour la division de chaîne de caractères.";
int c = 0;
char **arr = NULL;
c = split(s, ' ', &arr);
printf("trouvé %d jetons.\n", c);
for (i = 0; i < c; i++)
printf("chaîne #%d: %s\n", i, arr[i]);
return 0;
}
Lorsque je fais cela, cela ajoute soit trop à la dernière jeton, soit lui alloue trop de mémoire. Voici la sortie : a trouvé 10 jetons. chaîne #0 : Bonjour, chaîne #1 : cette chaîne #2 : est chaîne #3 : un chaîne #4 : test chaîne #5 : module chaîne #6 : pour chaîne #7 : le chaîne #8 : splitting.¢
Non testé, probablement incorrect, mais devrait vous donner un bon point de départ sur la façon dont cela devrait fonctionner:
*char[] str_split(char* str, char delim) {
int begin = 0;
int end = 0;
int j = 0;
int i = 0;
char *buf[NUM];
while (i < strlen(str)) {
if(*str == delim) {
buf[j] = malloc(sizeof(char) * (end-begin));
strncpy(buf[j], *(str + begin), (end-begin));
begin = end;
j++;
}
end++;
i++;
}
return buf;
}
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
32 votes
Vous pouvez utiliser la fonction
strtok
de la bibliothèque standard pour accomplir la même chose.0 votes
stackoverflow.com/questions/8461170/…
0 votes
Un commentaire... le point clé d'une fonction de la famille
strtok()
est de comprendre lesvariables statiques
en C, c'est-à-dire comment elles se comportent entre deux appels de fonction successifs dans lesquels elles sont utilisées. Voici mon code ci-dessous.0 votes
strtok
n'est pas une solution pour ce problème pour plusieurs raisons : il modifie la chaîne source, il a un état statique caché qui le rend non réentrant, il traitera les séquences de délimiteurs comme un seul délimiteur, ce qui semble incorrect pour,
, et par conséquent ne séparera pas les chaînes vides au début, au milieu ou à la fin de,X,,Y,
. Ne pas utiliserstrtok
.