Comment écrire une fonction pour scinder et renvoyer un tableau pour une chaîne 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 scinder une chaîne (et spécifier le délimiteur à utiliser). Notez que strtok()
modifiera la chaîne qui y est passée. Si la chaîne d'origine est requise ailleurs, faites-en une copie et transmettez-la à strtok()
.
MODIFIER:
Exemple (notez qu'il ne gère pas les délimiteurs consécutifs, "JAN , FEV, MAR" par exemple):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
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;
/* Count how many elements will be extracted. */
while (*tmp)
{
if (a_delim == *tmp)
{
count++;
last_comma = tmp;
}
tmp++;
}
/* Add space for trailing token. */
count += last_comma < (a_str + strlen(a_str) - 1);
/* Add space for terminating null string so caller
knows where the list of returned strings ends. */
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]
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.
Les parenthèses supplémentaires sont un élément stylistique indiquant 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 un littéral de chaîne, vous voudriez d'abord en faire une copie:
// More general pattern:
const char *my_str_literal = "JAN,FEB,MAR";
char *token, *str, *tofree;
tofree = str = strdup(my_str_literal); // We own str's memory now.
while ((token = strsep(&str, ","))) my_fn(token);
free(tofree);
Si deux délimiteurs apparaissent ensemble dans str
, vous obtiendrez une valeur token
qui correspond à la chaîne vide. La valeur de str
est modifiée en ce que chaque délimiteur rencontré est écrasé par un octet nul - une autre bonne raison de copier la chaîne analysée en premier.
Dans un commentaire, quelqu'un a suggéré que strtok
est préférable à strsep
car strtok
est plus portable. Ubuntu et Mac OS X ont strsep
; Il est prudent de deviner que d'autres systèmes Unixy le font également. Il manque à Windows strsep
, mais il a strbrk
qui permet cette substitution courte et douce 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;
}
Ici est 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 pour remplacer strtok
.
Ce code devrait vous mettre dans la bonne direction.
int main(void) {
char st[] ="Where there is will, there is a way.";
char *ch;
ch = strtok(st, " ");
while (ch != NULL) {
printf("%s\n", ch);
ch = strtok(NULL, " ,");
}
getch();
return 0;
}
La méthode ci-dessous fera tout le travail (allocation de mémoire, compter la longueur) pour vous. Vous trouverez plus d'informations et une description ici - Implémentation de la méthode Java String.split () pour scinder la 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 = "Hello, this is a test module for the string splitting.";
int c = 0;
char **arr = NULL;
c = split(s, ' ', &arr);
printf("found %d tokens.\n", c);
for (i = 0; i < c; i++)
printf("string #%d: %s\n", i, arr[i]);
return 0;
}
Dans l'exemple ci-dessus, il existe un moyen de renvoyer un tableau de chaînes terminées par un caractère nul (comme vous le souhaitez) à la place de la chaîne. Cela ne permettrait cependant pas de passer une chaîne littérale, car elle devrait être modifiée par la fonction:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char** str_split( char* str, char delim, int* numSplits )
{
char** ret;
int retLen;
char* c;
if ( ( str == NULL ) ||
( delim == '\0' ) )
{
/* Either of those will cause problems */
ret = NULL;
retLen = -1;
}
else
{
retLen = 0;
c = str;
/* Pre-calculate number of elements */
do
{
if ( *c == delim )
{
retLen++;
}
c++;
} while ( *c != '\0' );
ret = malloc( ( retLen + 1 ) * sizeof( *ret ) );
ret[retLen] = NULL;
c = str;
retLen = 1;
ret[0] = str;
do
{
if ( *c == delim )
{
ret[retLen++] = &c[1];
*c = '\0';
}
c++;
} while ( *c != '\0' );
}
if ( numSplits != NULL )
{
*numSplits = retLen;
}
return ret;
}
int main( int argc, char* argv[] )
{
const char* str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
char* strCpy;
char** split;
int num;
int i;
strCpy = malloc( strlen( str ) * sizeof( *strCpy ) );
strcpy( strCpy, str );
split = str_split( strCpy, ',', &num );
if ( split == NULL )
{
puts( "str_split returned NULL" );
}
else
{
printf( "%i Results: \n", num );
for ( i = 0; i < num; i++ )
{
puts( split[i] );
}
}
free( split );
free( strCpy );
return 0;
}
Il y a probablement une manière plus ordonnée de le faire, mais vous voyez l'idée.
Voici mes deux cents:
int split (const char *txt, char delim, char ***tokens)
{
int *tklen, *t, count = 1;
char **arr, *p = (char *) txt;
while (*p != '\0') if (*p++ == delim) count += 1;
t = tklen = calloc (count, sizeof (int));
for (p = (char *) txt; *p != '\0'; p++) *p == delim ? *t++ : (*t)++;
*tokens = arr = malloc (count * sizeof (char *));
t = tklen;
p = *arr++ = calloc (*(t++) + 1, sizeof (char *));
while (*txt != '\0')
{
if (*txt == delim)
{
p = *arr++ = calloc (*(t++) + 1, sizeof (char *));
txt++;
}
else *p++ = *txt++;
}
free (tklen);
return count;
}
Utilisation:
char **tokens;
int count, i;
const char *str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
count = split (str, ',', &tokens);
for (i = 0; i < count; i++) printf ("%s\n", tokens[i]);
/* freeing tokens */
for (i = 0; i < count; i++) free (tokens[i]);
free (tokens);
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
/**
* splits str on delim and dynamically allocates an array of pointers.
*
* On error -1 is returned, check errno
* On success size of array is returned, which may be 0 on an empty string
* or 1 if no delim was found.
*
* You could rewrite this to return the char ** array instead and upon NULL
* know it's an allocation problem but I did the triple array here. Note that
* upon the hitting two delim's in a row "foo,,bar" the array would be:
* { "foo", NULL, "bar" }
*
* You need to define the semantics of a trailing delim Like "foo," is that a
* 2 count array or an array of one? I choose the two count with the second entry
* set to NULL since it's valueless.
* Modifies str so make a copy if this is a problem
*/
int split( char * str, char delim, char ***array, int *length ) {
char *p;
char **res;
int count=0;
int k=0;
p = str;
// Count occurance of delim in string
while( (p=strchr(p,delim)) != NULL ) {
*p = 0; // Null terminate the deliminator.
p++; // Skip past our new null
count++;
}
// allocate dynamic array
res = calloc( 1, count * sizeof(char *));
if( !res ) return -1;
p = str;
for( k=0; k<count; k++ ){
if( *p ) res[k] = p; // Copy start of string
p = strchr(p, 0 ); // Look for next null
p++; // Start of next string
}
*array = res;
*length = count;
return 0;
}
char str[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,";
int main() {
char **res;
int k=0;
int count =0;
int rc;
rc = split( str, ',', &res, &count );
if( rc ) {
printf("Error: %s errno: %d \n", strerror(errno), errno);
}
printf("count: %d\n", count );
for( k=0; k<count; k++ ) {
printf("str: %s\n", res[k]);
}
free(res );
return 0;
}
Essayez d'utiliser ceci.
char** strsplit(char* str, const char* delim){
char** res = NULL;
char* part;
int i = 0;
char* aux = strdup(str);
part = strdup(strtok(aux, delim));
while(part){
res = (char**)realloc(res, (i + 1) * sizeof(char*));
*(res + i) = strdup(part);
part = strdup(strtok(NULL, delim));
i++;
}
res = (char**)realloc(res, i * sizeof(char*));
*(res + i) = NULL;
return res;
}
Ci-dessous, mon implémentation strtok()
de Bibliothèque zString . zstring_strtok()
diffère de la fonction strtok()
de la bibliothèque standard en ce qui concerne le traitement des délimiteurs consécutifs.
Il suffit de regarder le code ci-dessous, bien sûr, vous aurez une idée de la façon dont cela fonctionne (j'ai essayé d'utiliser autant de commentaires que possible)
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;
}
Vous trouverez ci-dessous un exemple d'utilisation ...
Example Usage
char str[] = "A,B,,,C";
printf("1 %s\n",zstring_strtok(s,","));
printf("2 %s\n",zstring_strtok(NULL,","));
printf("3 %s\n",zstring_strtok(NULL,","));
printf("4 %s\n",zstring_strtok(NULL,","));
printf("5 %s\n",zstring_strtok(NULL,","));
printf("6 %s\n",zstring_strtok(NULL,","));
Example Output
1 A
2 B
3 ,
4 ,
5 C
6 (null)
La bibliothèque peut être téléchargée à partir de Github https://github.com/fnoyanisi/zString
Cette fonction prend une chaîne char * et la scinde par le déliminateur. Il peut y avoir plusieurs déliminateurs à la suite. Notez que la fonction modifie la chaîne d'origine. Vous devez d'abord faire une copie de la chaîne d'origine si vous souhaitez que l'original reste inchangé. Cette fonction n'utilise aucun appel de fonction cstring, elle pourrait donc être un peu plus rapide que les autres. Si vous ne vous préoccupez pas de l'allocation de mémoire, vous pouvez allouer des sous-chaînes en haut de la fonction de taille strlen (src_str)/2 et (comme dans le cas de la "version" c ++ mentionnée), ignorer la moitié inférieure de la fonction. Si vous procédez ainsi, la fonction est réduite à O (N), mais le moyen d'optimisation de la mémoire illustré ci-dessous est O (2N).
La fonction:
char** str_split(char *src_str, const char deliminator, size_t &num_sub_str){
//replace deliminator's with zeros and count how many
//sub strings with length >= 1 exist
num_sub_str = 0;
char *src_str_tmp = src_str;
bool found_delim = true;
while(*src_str_tmp){
if(*src_str_tmp == deliminator){
*src_str_tmp = 0;
found_delim = true;
}
else if(found_delim){ //found first character of a new string
num_sub_str++;
found_delim = false;
//sub_str_vec.Push_back(src_str_tmp); //for c++
}
src_str_tmp++;
}
printf("Start - found %d sub strings\n", num_sub_str);
if(num_sub_str <= 0){
printf("str_split() - no substrings were found\n");
return(0);
}
//if you want to use a c++ vector and Push onto it, the rest of this function
//can be omitted (obviously modifying input parameters to take a vector, etc)
char **sub_strings = (char **)malloc( (sizeof(char*) * num_sub_str) + 1);
const char *src_str_terminator = src_str_tmp;
src_str_tmp = src_str;
bool found_null = true;
size_t idx = 0;
while(src_str_tmp < src_str_terminator){
if(!*src_str_tmp) //found a NULL
found_null = true;
else if(found_null){
sub_strings[idx++] = src_str_tmp;
//printf("sub_string_%d: [%s]\n", idx-1, sub_strings[idx-1]);
found_null = false;
}
src_str_tmp++;
}
sub_strings[num_sub_str] = NULL;
return(sub_strings);
}
Comment l'utiliser:
char months[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
char *str = strdup(months);
size_t num_sub_str;
char **sub_strings = str_split(str, ',', num_sub_str);
char *endptr;
if(sub_strings){
for(int i = 0; sub_strings[i]; i++)
printf("[%s]\n", sub_strings[i]);
}
free(sub_strings);
free(str);
Il s'agit d'une fonction de fractionnement de chaîne pouvant gérer les délimiteurs multi-caractères. Notez que si le délimiteur est plus long que la chaîne en cours de fractionnement, buffer
et stringLengths
seront définis sur (void *) 0
et numStrings
seront définis sur 0
.
Cet algorithme a été testé et fonctionne. (Avertissement: il n'a pas été testé pour les chaînes non-ASCII et suppose que l'appelant a fourni des paramètres valides)
void splitString(const char *original, const char *delimiter, char ** & buffer, int & numStrings, int * & stringLengths){
const int lo = strlen(original);
const int ld = strlen(delimiter);
if(ld > lo){
buffer = (void *)0;
numStrings = 0;
stringLengths = (void *)0;
return;
}
numStrings = 1;
for(int i = 0;i < (lo - ld);i++){
if(strncmp(&original[i], delimiter, ld) == 0) {
i += (ld - 1);
numStrings++;
}
}
stringLengths = (int *) malloc(sizeof(int) * numStrings);
int currentStringLength = 0;
int currentStringNumber = 0;
int delimiterTokenDecrementCounter = 0;
for(int i = 0;i < lo;i++){
if(delimiterTokenDecrementCounter > 0){
delimiterTokenDecrementCounter--;
} else if(i < (lo - ld)){
if(strncmp(&original[i], delimiter, ld) == 0){
stringLengths[currentStringNumber] = currentStringLength;
currentStringNumber++;
currentStringLength = 0;
delimiterTokenDecrementCounter = ld - 1;
} else {
currentStringLength++;
}
} else {
currentStringLength++;
}
if(i == (lo - 1)){
stringLengths[currentStringNumber] = currentStringLength;
}
}
buffer = (char **) malloc(sizeof(char *) * numStrings);
for(int i = 0;i < numStrings;i++){
buffer[i] = (char *) malloc(sizeof(char) * (stringLengths[i] + 1));
}
currentStringNumber = 0;
currentStringLength = 0;
delimiterTokenDecrementCounter = 0;
for(int i = 0;i < lo;i++){
if(delimiterTokenDecrementCounter > 0){
delimiterTokenDecrementCounter--;
} else if(currentStringLength >= stringLengths[currentStringNumber]){
buffer[currentStringNumber][currentStringLength] = 0;
delimiterTokenDecrementCounter = ld - 1;
currentStringLength = 0;
currentStringNumber++;
} else {
buffer[currentStringNumber][currentStringLength] = (char)original[i];
currentStringLength++;
}
}
buffer[currentStringNumber][currentStringLength] = 0;
}
Exemple de code:
int main(){
const char *string = "STRING-1 DELIM string-2 DELIM sTrInG-3";
char **buffer;
int numStrings;
int * stringLengths;
splitString(string, " DELIM ", buffer, numStrings, stringLengths);
for(int i = 0;i < numStrings;i++){
printf("String: %s\n", buffer[i]);
}
}
Bibliothèques:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
Cette méthode optimisée crée (ou met à jour un tableau existant) de pointeurs dans * result et renvoie le nombre d'éléments dans * count.
Utilisez "max" pour indiquer le nombre maximum de chaînes que vous attendez (lorsque vous spécifiez un tableau existant ou tout autre reaseon), sinon définissez-le sur 0
Pour comparer à une liste de délimiteurs, définissez-le comme un caractère * et remplacez la ligne:
if (str[i]==delim) {
avec les deux lignes suivantes:
char *c=delim; while(*c && *c!=str[i]) c++;
if (*c) {
Prendre plaisir
#include <stdlib.h>
#include <string.h>
char **split(char *str, size_t len, char delim, char ***result, unsigned long *count, unsigned long max) {
size_t i;
char **_result;
// there is at least one string returned
*count=1;
_result= *result;
// when the result array is specified, fill it during the first pass
if (_result) {
_result[0]=str;
}
// scan the string for delimiter, up to specified length
for (i=0; i<len; ++i) {
// to compare against a list of delimiters,
// define delim as a string and replace
// the next line:
// if (str[i]==delim) {
//
// with the two following lines:
// char *c=delim; while(*c && *c!=str[i]) c++;
// if (*c) {
//
if (str[i]==delim) {
// replace delimiter with zero
str[i]=0;
// when result array is specified, fill it during the first pass
if (_result) {
_result[*count]=str+i+1;
}
// increment count for each separator found
++(*count);
// if max is specified, dont go further
if (max && *count==max) {
break;
}
}
}
// when result array is specified, we are done here
if (_result) {
return _result;
}
// else allocate memory for result
// and fill the result array
*result=malloc((*count)*sizeof(char*));
if (!*result) {
return NULL;
}
_result=*result;
// add first string to result
_result[0]=str;
// if theres more strings
for (i=1; i<*count; ++i) {
// find next string
while(*str) ++str;
++str;
// add next string to result
_result[i]=str;
}
return _result;
}
Exemple d'utilisation:
#include <stdio.h>
int main(int argc, char **argv) {
char *str="JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
char **result=malloc(6*sizeof(char*));
char **result2=0;
unsigned long count;
unsigned long count2;
unsigned long i;
split(strdup(str),strlen(str),',',&result,&count,6);
split(strdup(str),strlen(str),',',&result2,&count2,0);
if (result)
for (i=0; i<count; ++i) {
printf("%s\n",result[i]);
}
printf("\n");
if (result2)
for (i=0; i<count2; ++i) {
printf("%s\n", result2[i]);
}
return 0;
}
Non testé, probablement faux, mais devrait vous donner une bonne longueur d'avance 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;
}
Mon approche consiste à balayer la chaîne et à laisser les pointeurs pointer sur chaque caractère après les déliminateurs (et le premier caractère), en même temps, attribuer les apparences de déliminateur dans la chaîne à '\ 0'.
Commencez par faire une copie de la chaîne originale (car elle est constante), puis récupérez le nombre de scissions en l’analysant puis transmettez-la au paramètre de pointeur len . Après cela, pointez le premier pointeur de résultat sur le pointeur de chaîne de copie, puis analysez la chaîne de copie: une fois que vous rencontrez un délimiteur, affectez-le à '\ 0' afin que la chaîne de résultat précédente soit terminée et pointez le pointeur suivant sur la chaîne suivante. pointeur de caractère.
char** split(char* a_str, const char a_delim, int* len){
char* s = (char*)malloc(sizeof(char) * strlen(a_str));
strcpy(s, a_str);
char* tmp = a_str;
int count = 0;
while (*tmp != '\0'){
if (*tmp == a_delim) count += 1;
tmp += 1;
}
*len = count;
char** results = (char**)malloc(count * sizeof(char*));
results[0] = s;
int i = 1;
while (*s!='\0'){
if (*s == a_delim){
*s = '\0';
s += 1;
results[i++] = s;
}
else s += 1;
}
return results;
}
Mon code (testé):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int dtmsplit(char *str, const char *delim, char ***array, int *length ) {
int i=0;
char *token;
char **res = (char **) malloc(0 * sizeof(char *));
/* get the first token */
token = strtok(str, delim);
while( token != NULL )
{
res = (char **) realloc(res, (i + 1) * sizeof(char *));
res[i] = token;
i++;
token = strtok(NULL, delim);
}
*array = res;
*length = i;
return 1;
}
int main()
{
int i;
int c = 0;
char **arr = NULL;
int count =0;
char str[80] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
c = dtmsplit(str, ",", &arr, &count);
printf("Found %d tokens.\n", count);
for (i = 0; i < count; i++)
printf("string #%d: %s\n", i, arr[i]);
return(0);
}
Résultat:
Found 12 tokens.
string #0: JAN
string #1: FEB
string #2: MAR
string #3: APR
string #4: MAY
string #5: JUN
string #6: JUL
string #7: AUG
string #8: SEP
string #9: OCT
string #10: NOV
string #11: DEC
Ma version:
int split(char* str, const char delimeter, char*** args) {
int cnt = 1;
char* t = str;
while (*t == delimeter) t++;
char* t2 = t;
while (*(t2++))
if (*t2 == delimeter && *(t2 + 1) != delimeter && *(t2 + 1) != 0) cnt++;
(*args) = malloc(sizeof(char*) * cnt);
for(int i = 0; i < cnt; i++) {
char* ts = t;
while (*t != delimeter && *t != 0) t++;
int len = (t - ts + 1);
(*args)[i] = malloc(sizeof(char) * len);
memcpy((*args)[i], ts, sizeof(char) * (len - 1));
(*args)[i][len - 1] = 0;
while (*t == delimeter) t++;
}
return cnt;
}
Je pense que la solution suivante est idéale:
Explication du code:
token
pour stocker l'adresse et les longueurs des jetonsstr
est entièrement constitué de séparateurs. Il y a donc strlen(str) + 1
Jetons, tous des chaînes videsstr
en enregistrant l'adresse et la longueur de chaque jetonNULL
sentinelmemcpy
car il est plus rapide que strcpy
et nous connaissons Les longueurs.typedef struct {
const char *start;
size_t len;
} token;
char **split(const char *str, char sep)
{
char **array;
unsigned int start = 0, stop, toks = 0, t;
token *tokens = malloc((strlen(str) + 1) * sizeof(token));
for (stop = 0; str[stop]; stop++) {
if (str[stop] == sep) {
tokens[toks].start = str + start;
tokens[toks].len = stop - start;
toks++;
start = stop + 1;
}
}
/* Mop up the last token */
tokens[toks].start = str + start;
tokens[toks].len = stop - start;
toks++;
array = malloc((toks + 1) * sizeof(char*));
for (t = 0; t < toks; t++) {
/* Calloc makes it nul-terminated */
char *token = calloc(tokens[t].len + 1, 1);
memcpy(token, tokens[t].start, tokens[t].len);
array[t] = token;
}
/* Add a sentinel */
array[t] = NULL;
free(tokens);
return array;
}
Remarque malloc
vérification omise par souci de concision.
En général, je ne renverrais pas un tableau d'indicateurs char *
à partir d'une fonction divisée comme celle-ci, car l'appelant aurait beaucoup de responsabilités pour le libérer correctement. Une interface que je préfère consiste à autoriser l'appelant à transmettre une fonction de rappel et à l'appeler pour chaque jeton, comme je l'ai décrit ici: Fractionner une chaîne dans C .
Exploser et imploser - la chaîne initiale reste intacte, allocation de mémoire dynamique
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct
{
uintptr_t ptr;
int size;
} token_t;
int explode(char *str, int slen, const char *delimiter, token_t **tokens)
{
int i = 0, c1 = 0, c2 = 0;
for(i = 0; i <= slen; i++)
{
if(str[i] == *delimiter)
{
c1++;
}
}
if(c1 == 0)
{
return -1;
}
*tokens = (token_t*)calloc((c1 + 1), sizeof(token_t));
((*tokens)[c2]).ptr = (uintptr_t)str;
i = 0;
while(i <= slen)
{
if((str[i] == *delimiter) || (i == slen))
{
((*tokens)[c2]).size = (int)((uintptr_t)&(str[i]) - (uintptr_t)(((*tokens)[c2]).ptr));
if(i < slen)
{
c2++;
((*tokens)[c2]).ptr = (uintptr_t)&(str[i + 1]);
}
}
i++;
}
return (c1 + 1);
}
char* implode(token_t *tokens, int size, const char *delimiter)
{
int i, len = 0;
char *str;
for(i = 0; i < len; i++)
{
len += tokens[i].size + 1;
}
str = (char*)calloc(len, sizeof(char));
len = 0;
for(i = 0; i < size; i++)
{
memcpy((void*)&str[len], (void*)tokens[i].ptr, tokens[i].size);
len += tokens[i].size;
str[(len++)] = *delimiter;
}
str[len - 1] = '\0';
return str;
}
Usage:
int main(int argc, char **argv)
{
int i, c;
char *exp = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
token_t *tokens;
char *imp;
printf("%s\n", exp);
if((c = explode(exp, strlen(exp), ",", &tokens)) > 0)
{
imp = implode(tokens, c, ",");
printf("%s\n", imp);
for(i = 0; i < c; i++)
{
printf("%.*s, %d\n", tokens[i].size, (char*)tokens[i].ptr, tokens[i].size);
}
}
free((void*)tokens);
free((void*)imp);
return 0;
}
Pour: Hassan A. El-Seoudy
Votre billet est fermé, je ne peux donc pas y répondre ^^ '. Mais vous pouvez essayer ceci:
'
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int countChar(char *str)
{
int count;
int i;
i = 0;
count = 0;
while (str[i] != '=') // our delimiter character
{
i++;
count++;
}
return (count);
}
void split(char *str)
{
int i;
int j;
int count;
int restCount;
char *str1;
char *str2;
i = 0;
j = 0;
count = countChar(str) - 1; // we have our str1 lenght, -1 for the ' '
restCount = (strlen(str) - count) -1; // we have our str2 legnht, -1 for the ' '
str1 = malloc(sizeof(char) * count);
str2 = malloc(sizeof(char) * restCount);
while(i < count)
{
str1[i] = str[i++];
}
i = i + 2; // to jump directly to the first char of our str2 (no ' = ')
while (str[i])
{
str2[j++] = str[i++];
}
printf("str1 = %s, str2 = %s\n", str1, str2);
}
int main()
{
char *str = "Xo = 100k";
split(str);
return (0);
}'
Il y a quelques problèmes avec strtok () répertoriés ici: http://benpfaff.org/writings/clc/strtok.html
Par conséquent, il vaut mieux éviter strtok.
Maintenant, considérons une chaîne contenant un champ vide comme suit:
char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here
Vous pouvez utiliser la fonction simple pour pouvoir convertir String au format CSV afin de les lire dans un tableau float:
int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim);
Nous avons spécifié le délimiteur ici comme une virgule. Cela fonctionne avec un autre délimiteur à un seul caractère.
Veuillez trouver le Usage ci-dessous:
#include <stdio.h>
#include <stdlib.h>
int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim);
void main()
{
char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here
float floatArr[10]; // specify size of float array here
int totalValues = 0;
char myDelim = ','; // specify delimiter here
printf("myCSVString == %s \n",&myCSVString[0]);
totalValues = strCSV2Float(&floatArr[0] , &myCSVString[0], myDelim); // call the function here
int floatValueCount = 0;
for (floatValueCount = 0 ; floatValueCount < totalValues ; floatValueCount++)
{
printf("floatArr[%d] = %f\n",floatValueCount , floatArr[floatValueCount]);
}
}
int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim)
{
int strLen = 0;
int commaCount =0; // count the number of commas
int commaCountOld =0; // count the number of commas
int wordEndChar = 0;
int wordStartChar = -1;
int wordLength =0;
for(strLen=0; myCSVStringing[strLen] != '\0'; strLen++) // first get the string length
{
if ( (myCSVStringing[strLen] == delim) || ( myCSVStringing[strLen+1] == '\0' ))
{
commaCount++;
wordEndChar = strLen;
}
if ( (commaCount - commaCountOld) > 0 )
{
int aIter =0;
wordLength = (wordEndChar - wordStartChar);
char Word[55] = "";
for (aIter = 0; aIter < wordLength; aIter++)
{
Word[aIter] = myCSVStringing[strLen-wordLength+aIter+1];
}
if (Word[aIter-1] == delim)
Word[aIter-1] = '\0';
// printf("\n");
Word[wordLength] = '\0';
strFloatArray[commaCount-1] = atof(&Word[0]);
wordLength = 0;
wordStartChar = wordEndChar;
commaCountOld = commaCount;
}
}
return commaCount;
}
Output est comme suit:
myCSVString == -1.4,2.6,,-0.24,1.26
floatArr[0] = -1.400000
floatArr[1] = 2.600000
floatArr[2] = 0.000000
floatArr[3] = -0.240000
floatArr[4] = 1.260000
Si vous souhaitez utiliser une bibliothèque externe, je ne peux pas recommander bstrlib
suffisant. Cela nécessite un peu plus de configuration, mais il est plus facile à utiliser à long terme.
Par exemple, divisez la chaîne ci-dessous pour créer une bstring
avec l'appel bfromcstr()
. (Une bstring
entoure un tampon de caractères) ..____ Ensuite, divisez la chaîne en virgules, en enregistrant le résultat dans un struct bstrList
, qui contient les champs qty
et un tableau entry
, qui est un tableau de bstring
s.
bstrlib
a beaucoup d'autres fonctions à utiliser sur bstring
s
C'est de la tarte...
#include "bstrlib.h"
#include <stdio.h>
int main() {
int i;
char *tmp = "Hello,World,sak";
bstring bstr = bfromcstr(tmp);
struct bstrList *blist = bsplit(bstr, ',');
printf("num %d\n", blist->qty);
for(i=0;i<blist->qty;i++) {
printf("%d: %s\n", i, bstr2cstr(blist->entry[i], '_'));
}
}
static int count_token(char *iptr, char delim) {
int token_count = 0;
while (*iptr && isspace(*iptr))
iptr++;
while (*iptr) {
if ((*iptr != delim)) {
token_count++;
while (*iptr && (*iptr != delim))
iptr++;
}
else {
iptr++;
}
}
return token_count;
}
static char** split(char* input, int* argc){
char** argv;
int token_count = count_token(input, ' ');
argv = (char**)malloc(sizeof(char*)*token_count);
int i = 0;
char *token = strtok(input, " ");
while(token) {
puts(token);
argv[i] = strdup(token);
token = strtok(NULL, " ");
i++;
}
assert(i == token_count);
*argc = token_count;
return argv;
}
Encore une autre réponse (cela a été déplacé ici de ici ):
Essayez d'utiliser la fonction strtok:
voir les détails sur ce sujet ici ou ici
Le problème ici est que vous devez traiter la words
immédiatement. Si vous voulez le stocker dans un tableau, vous devez lui allouer le correct size
qui est inconnu.
Donc par exemple:
char **Split(char *in_text, char *in_sep)
{
char **ret = NULL;
int count = 0;
char *tmp = strdup(in_text);
char *pos = tmp;
// This is the pass ONE: we count
while ((pos = strtok(pos, in_sep)) != NULL)
{
count++;
pos = NULL;
}
// NOTE: the function strtok changes the content of the string! So we free and duplicate it again!
free(tmp);
pos = tmp = strdup(in_text);
// We create a NULL terminated array hence the +1
ret = calloc(count+1, sizeof(char*));
// TODO: You have to test the `ret` for NULL here
// This is the pass TWO: we store
count = 0;
while ((pos = strtok(pos, in_sep)) != NULL)
{
ret[count] = strdup(pos);
count++;
pos = NULL;
}
free(tmp);
return count;
}
// Use this to free
void Free_Array(char** in_array)
{
char *pos = in_array;
while (pos[0] != NULL)
{
free(pos[0]);
pos++;
}
free(in_array);
}
Note: Nous utilisons la même boucle et la même fonction pour calculer les comptes (passe un) et pour faire les copies (passe deux), afin d’éviter les problèmes d’allocation.
Note 2: Vous pouvez utiliser une autre implémentation de strtok, les raisons mentionnées dans des messages séparés.
Vous pouvez utiliser ceci comme:
int main(void)
{
char **array = Split("Hello World!", " ");
// Now you have the array
// ...
// Then free the memory
Free_Array(array);
array = NULL;
return 0;
}
(Je ne l'ai pas testé, merci de me le faire savoir si cela ne fonctionne pas!)