Cadena dividida con delimitadores en C

20 minutos de lectura

avatar de usuario
namco

¿Cómo escribo una función para dividir y devolver una matriz para una cadena con delimitadores en el lenguaje de programación C?

char* str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
str_split(str,',');

  • Puedes usar el strtok función de la biblioteca estándar para lograr lo mismo.

    –Daniel Kamil Kozar

    9 de febrero de 2012 a las 12:08

  • stackoverflow.com/questions/8461170/…

    – PIXY AZUL

    9 de febrero de 2012 a las 12:56

  • Un comentario… el punto clave para un strtok() la funcion de la familia es la comprension static variables en C. es decir, cómo se comportan entre las sucesivas llamadas de funciones en las que se utilizan. Ver mi código a continuación

    – fnisi

    21 de febrero de 2016 a las 19:48

  • strtok es no una solución para este problema por varias razones: modifica la cadena de origen, tiene un estado estático oculto que hace que no vuelva a entrar, manejará secuencias de delimitadores como un solo delimitador, lo que parece incorrecto para ,y como consecuencia no dividirá las cadenas vacías al principio, en medio ni al final de ,X,,Y,. no usar strtok.

    – chqrlie

    28 de diciembre de 2021 a las 11:26

avatar de usuario
tyler

pienso strsep sigue siendo la mejor herramienta para esto:

while ((token = strsep(&str, ","))) my_fn(token);

Esa es literalmente una línea que divide una cadena.

Los paréntesis adicionales son un elemento estilístico para indicar que estamos probando intencionalmente el resultado de una asignación, no un operador de igualdad. ==.

Para que ese patrón funcione, token y str ambos tienen tipo char *. Si comenzó con un literal de cadena, primero querrá hacer una copia:

// 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 dos delimitadores aparecen juntos en strobtendrás un token valor que es la cadena vacía. El valor de str se modifica en el sentido de que cada delimitador encontrado se sobrescribe con un byte cero, otra buena razón para copiar primero la cadena que se analiza.

En un comentario, alguien sugirió que strtok es mejor que strsep porque strtok es más portátil. Ubuntu y Mac OS X tienen strsep; es seguro suponer que otros sistemas unixy también lo hacen. Windows carece strseppero tiene strbrk que permite este corto y dulce strsep reemplazo:

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;
}

Aquí hay una buena explicación de strsep contra strtok. Los pros y los contras pueden juzgarse subjetivamente; sin embargo, creo que es una señal reveladora de que strsep fue diseñado como un reemplazo para strtok.

  • Más precisamente sobre la portabilidad: es no POSIX 7pero derivado de BSD e implementado en glibc.

    – Ciro Santilli Путлер Капут 六四事

    20 de julio de 2015 a las 7:04

  • Estaba a punto de preguntar… El C de Pelle tiene strdup(), pero no strsep().

    – rdtsc

    3 de febrero de 2017 a las 2:18

  • por qué tofree es el que está libre y no str?

    – Sdlion

    17 mayo 2018 a las 17:20

  • no puedes liberar str porque su valor se puede cambiar mediante llamadas a strsep(). El valor de tofree apunta constantemente al inicio de la memoria que desea liberar.

    – Tyler

    18 mayo 2018 a las 18:10

avatar de usuario
saif mahmud

Traté de hacer uno muy simple. También estoy mostrando un ejemplo en main().

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

void split(char* inputArr, char** outputArr, char* delim) {
    
        char *temp;
        temp = strtok(inputArr, delim);

        for(int i = 0; temp != NULL; i++) {
            outputArr[i] = temp;
            temp = strtok(NULL, " ");
        }
}

int main(int argc, char **argv){
    
    /* check for proper arguments */
    
    if(argc != 2){
        printf("One Argument Expected\n");
    } else {

        printf("\n");
        /*---------main code starts here----------*/
        FILE * myScriptFile;
        myScriptFile = fopen(argv[1], "r");
        
        /* read txt file and split into array like java split() */
        
        int bufferLen = 100;
        char buffer[bufferLen];
        
        char *splitArr[100];        

        while(fgets(buffer, bufferLen, myScriptFile) != NULL){
            
            split(buffer, splitArr, " ");

            printf("Index 0 String: %s\n", splitArr[0]);
            printf("Index 1 String: %s\n", splitArr[1]);
            printf("Index 2 String: %s\n", splitArr[2]);
            printf("Index 3 String: %s\n", splitArr[3]);
        }
        fclose(myScriptFile);
    }
    printf("\nProgram-Script Ended\n");
    return 0;
}

Suponga que un archivo .txt tiene

Hello this is test
Hello2 this is test2

ejecutarlo con un archivo .txt como parámetro daría

Index 0 String: Hello
Index 1 String: this
Index 2 String: is
Index 3 String: test

Index 0 String: Hello2
Index 1 String: this
Index 2 String: is
Index 3 String: test2

Ambas cosas strtok() y strsep() modificar la cadena de entrada. Podemos escribir una función para dividir la cadena en función de los delimitadores usando strspn() y strpbrk().

Algoritmo:

  1. Si la cadena de entrada no está vacía, vaya al paso 2, de lo contrario regrese null.
  2. Omita el separador, si lo hay al principio de la cadena, y registre la posición inicial de la palabra (usando strspn() para esto), llámalo start.
  3. Encuentre la siguiente posición del separador (o el final de la cadena si no existe más separador) desde el inicio actual encontrado en el paso anterior (usando strpbrk() para esto), llámalo end.
  4. Asignar memoria y copiar cadena de start para end en ese recuerdo.
  5. Ficha de retorno.

Ventaja:

  1. A salvo de amenazas.
  2. Maneja múltiples delimitadores.
  3. Portátil.
  4. No modifica la cadena de entrada, como strtok() y strsep() hace.

Implementación:

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

/*
 * alloc_str function allocates memory and copy substring
 * to allocated memory.
 */

static char * alloc_str (const char * start, const char * end) {
    if (!start || !end || (start >= end)) {
        return NULL;
    }

    char * tmp = malloc (end - start + 1);
    if (tmp) {
        memcpy (tmp, start, end - start);
        tmp[end - start] = '\0';
    } else {
        fprintf (stderr, "Failed to allocate memory\n");
        exit (EXIT_FAILURE);
    }

    return tmp;
}

/*
 * str_split function returns the next token which is sequences of contiguous
 * characters separated by any of the characters that are part of delimiters.
 *
 * Parameters: 
 * p_str : Address of pointer to the string that you want to split.
 * sep : A set of characters that delimit the pieces in the string.
 *
 * Behaviour is undefined if sep is not a pointer to a null-terminated string. 
 *
 * Return :
 * Returns the pointer to dynamically allocated memory where the token is copied.
 * If p_str is NULL or empty string, NULL is returned.
 */

char * str_split (char ** p_str, const char * sep) {
    char * token = NULL;

    if (*p_str && **p_str) {
        char * p_end;

        // skip separator
        *p_str += strspn(*p_str, sep);

        p_end = *p_str;

        // find separator
        p_end = strpbrk (p_end, sep);

        // strpbrk() returns null pointer if no such character
        // exists in the input string which is part of sep argument.
        if (!p_end) {
            p_end = *p_str + strlen (*p_str);
        }

        token = alloc_str (*p_str, p_end);
        *p_str = p_end;
    }

    return token;
}

/*==================================================*/
/*==================================================*/

/*
 * Just a helper function
 */

void token_helper (char * in_str, const char * delim) {
    printf ("\nInput string : ");

    if (in_str) printf ("\"%s\"\n", in_str);
    else printf ("NULL\n");

    if (delim) printf ("Delimiter : \"%s\"\n", delim);

    char * ptr = in_str;
    char * token = NULL;

    printf ("Tokens:\n");
    while ((token = str_split(&ptr, delim)) != NULL) {
        printf ("-> %s\n", token);
        /* You can assign this token to a pointer of an array of pointers
         * and return that array of pointers from this function.
         * Since, this is for demonstration purpose, I am 
         * freeing the allocated memory now.
         */
        free (token);
    }
}

/*
 * Driver function
 */

int main (void) {
    /* test cases */

    char string[100] = "hello world!";
    const char * delim = " ";
    token_helper (string, delim);

    strcpy (string, " hello world,friend of mine!");
    delim = " ,";
    token_helper (string, delim);

    strcpy (string, "Another string");
    delim = "-!";
    token_helper (string, delim);

    strcpy (string, "   one  more   -- string  !");
    delim = "- !";
    token_helper (string, delim); 

    strcpy (string, "");
    delim = " ";
    token_helper (string, delim);

    token_helper (NULL, "");

    strcpy (string, "hi");
    delim = " -$";
    token_helper (string, delim);

    strcpy (string, "Give papa a cup of proper coffee in a copper coffee cup.");
    delim = "cp";
    token_helper (string, delim);

    strcpy (string, "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC");
    delim = ",";
    token_helper (string, delim);

    return 0;
}

Producción:

# ./a.out

Input string : "hello world!"
Delimiter : " "
Tokens:
-> hello
-> world!

Input string : " hello world,friend of mine!"
Delimiter : " ,"
Tokens:
-> hello
-> world
-> friend
-> of
-> mine!

Input string : "Another string"
Delimiter : "-!"
Tokens:
-> Another string

Input string : "   one  more   -- string  !"
Delimiter : "- !"
Tokens:
-> one
-> more
-> string

Input string : ""
Delimiter : " "
Tokens:

Input string : NULL
Delimiter : ""
Tokens:

Input string : "hi"
Delimiter : " -$"
Tokens:
-> hi

Input string : "Give papa a cup of proper coffee in a copper coffee cup."
Delimiter : "cp"
Tokens:
-> Give 
-> a
-> a a 
-> u
->  of 
-> ro
-> er 
-> offee in a 
-> o
-> er 
-> offee 
-> u
-> .

Input string : "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC"
Delimiter : ","
Tokens:
-> JAN
-> FEB
-> MAR
-> APR
-> MAY
-> JUN
-> JUL
-> AUG
-> SEP
-> OCT
-> NOV
-> DEC

Ya sé que llegó tarde a la fiesta, pero aquí hay 2 funciones más para jugar y probablemente ajustarse aún más a sus necesidades (código fuente en la parte inferior del puesto)

Véase también el Notas de implementaciónmás abajo, para decidir qué función se adapta mejor a sus necesidades.

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>  // C99

// tokenize destructively
char **str_toksarray_alloc(
    char **strp,       /* InOut: pointer to the source non-constant c-string */
    const char *delim, /* c-string containing the delimiting chars */
    size_t *ntoks,     /* InOut: # of tokens to parse/parsed (NULL or *ntoks==0 for all tokens) */
    bool keepnulls     /* false ignores empty tokens, true includes them */
    );

// tokenize non-destructively
char **str_toksarray_alloc2(
   const char *str,    /* the source c-string */
   const char *delim,
   size_t *ntoks,
   bool keepnulls
   );

Notas de uso

Sus prototipos son casi idénticos, excepto por la cadena de origen (strp y strrespectivamente).

strp (puntero a cadena) es la dirección de una cadena C no constante ya asignada, que se tokenizará en el lugar. str es una cadena c que no está alterada (incluso puede ser una cadena literal). Por cuerda C me refiero a un nul-buffer terminado de caracteres. El resto de los argumentos son los mismos para ambas funciones.

Para analizar todos los tokens disponibles, silencio ntoks (es decir, configúrelo en 0 antes de pasarlo a cualquiera de las funciones o páselo como un NULL puntero). De lo contrario, las funciones analizan hasta *ntoks fichas, o hasta que no haya más fichas (lo que suceda primero). En cualquier caso, cuando ntoks es non-NULL se actualiza con el recuento de tokens analizados con éxito.

Nótese también que un no silenciado ntoks determina cuántos punteros se asignarán. Por lo tanto, si la cadena de origen contiene, digamos, 10 tokens y configuramos ntoks a 1000, terminaremos con 990 punteros asignados innecesariamente. Por otro lado, si la cadena de origen contiene, digamos, 1000 tokens pero solo necesitamos los primeros 10, configurando ntoks a 10 suena como una opción mucho más sabia.

Ambas funciones asignar y devolver una matriz de char-pointerspero str_toksarray_alloc() hace que apunten a los tokens en la propia cadena de origen modificada, mientras que str_toksarray_alloc2() los hace apuntar a copias asignadas dinámicamente de los tokens (ese 2 al final de su nombre indica los 2 niveles de asignación).

La matriz devuelta se adjunta con un NULL puntero centinela, que no se tiene en cuenta en el valor devuelto de ntoks (dicho de otro modo, cuando non-NULL, ntoks devuelve a la persona que llama la longitud de la matriz devuelta, no su tamaño de primer nivel).

Cuando keepnulls se establece en true, las fichas resultantes son similares a lo que esperaríamos de la strsep() función. Principalmente significa que los delimitadores consecutivos en la cadena de origen producen tokens vacíos (nulos), y si delim es una cadena c vacía o ninguno de sus caracteres delimitadores contenidos se encontró en la cadena de origen, el resultado es solo 1 token: la cadena de origen. Contrariamente a strsep()los tokens vacíos se pueden ignorar configurando keepnulls para false.

Ha fallado Las llamadas de las funciones se pueden identificar comparando su valor de retorno con NULLo comprobando el valor devuelto de ntoks contra 0 (siempre que ntoks era non-NULL). Sugiero verificar siempre contra fallas antes de intentar acceder a la matriz devuelta, porque las funciones incluyen verificaciones de cordura que pueden posponer fallas inmediatas (por ejemplo, pasar un NULL puntero como cadena de origen).

Sobre el éxito, la persona que llama debe liberar la matriz cuando haya terminado. Para str_toksarray_alloc()un simple gratis() es suficiente. Para str_toksarray_alloc2() se trata de un bucle, debido al segundo nivel de asignación. Él NULL centinela (o el valor devuelto de un non-NULL ntoks) hace que esto sea trivial, pero también estoy proporcionando una toksarray_free2() función a continuación, para todas las abejas perezosas por ahí 🙂

A continuación se muestran ejemplos simplificados que utilizan ambas funciones.

Deberes:

const char *src = ";b,test,Tèst,;;cd;ελληνικά,nørmälize,;string to";
const char *delim = ";,";
bool keepnulls = true;
size_t ntoks = 0;

str_toksarray_alloc():

// destructive (use copy of src)

char *scopy = strdup( src );
if (!scopy) { ... };          // handle strdup failure

printf( "%s\n", src );
char **arrtoks = str_toksarray_alloc( &scopy, delim, &ntoks, keepnulls );
printf( "%lu tokens read\n", ntoks );
if ( arrtoks ) {
    for (int i=0; arrtoks[i]; i++) {
        printf( "%d: %s\n", i, arrtoks[i] );
    }
}
free( scopy );
free( arrtoks );

/* OUTPUT
;b,test,Tèst,;;cd;ελληνικά,nørmälize,;string to
11 tokens read
0:
1: b
2: test
3: Tèst
4:
5:
6: cd
7: ελληνικά
8: nørmälize
9:
10: string to
 */

str_toksarray_alloc2():

// non-destructive

keepnulls = false;    // reject empty tokens

printf( "%s\n", src );
arrtoks = str_toksarray_alloc2( src, delim, &ntoks, keepnulls );
printf( "%lu tokens read\n", ntoks );
if ( arrtoks ) {
    for (int i=0; arrtoks[i]; i++) {
        printf( "%d: %s\n", i, arrtoks[i] );
    }
}
toksarray_free2( arrtoks );                     // dangling arrtoks
// or: arrtoks = toksarray_free2( arrtoks );    // non-dangling artoks

/* OUTPUT
;b,test,Tèst,;;cd;ελληνικά,nørmälize,;string to
7 tokens read
0: b
1: test
2: Tèst
3: cd
4: ελληνικά
5: nørmälize
6: string to
*/

Notas de implementación

Ambas funciones utilizan strsep() por la tokenización que los hace a salvo de amenazas, pero no es una función estándar. Si no se proporciona, siempre puede usar una implementación de código abierto (como GNU o de manzana por ejemplo). Lo mismo ocurre con la función strdup() que se usa en str_toksarray_alloc2() (su implementación es trivial, pero de nuevo aquí está GNU y de manzana por ejemplo).

Un efecto secundario del uso strsep() en str_toksarray_alloc() es que el puntero de inicio de la cadena de origen sigue moviéndose al siguiente token en cada paso del ciclo de análisis. Esto significa que la persona que llama no podrá liberar la cadena analizada, a menos que haya guardado la dirección de inicio en un puntero adicional. Les ahorramos la molestiahaciéndolo localmente en la función, usando el strpSaved puntero. str_toksarray_alloc2() no se ve afectado por esto, porque no toca la cadena de origen.

Una diferencia principal entre las 2 funciones es que str_toksarray_alloc() no asigna memoria para los tokens encontrados. Más bien asigna espacio solo para los punteros de matriz y los establece apuntando directamente a la cadena de origen. Esto funciona porque strsep() nul-termina los tokens encontrados en el lugar. Esta dependencia puede complicar su código de soporte, pero con cadenas grandes también puede marcar una gran diferencia en el rendimiento. Si conservar la cadena de origen no es importante, también puede marcar una gran diferencia en el consumo de memoria.

Por otro lado, str_toksarray_alloc2() asigna y devuelve una matriz autosuficiente de copias de los tokens asignadas dinámicamente, sin más dependencias. Lo hace, en primer lugar, creando la matriz a partir de un duplicado local de la cadena de origen y, en segundo lugar, duplicando el contenido de los tokens reales en la matriz. Esto es mucho más lento y deja una huella de memoria mucho mayor en comparación con str_toksarray_alloc(), pero no tiene más dependencias y no establece requisitos especiales para la naturaleza de la cadena de origen. Esto hace que sea más fácil escribir código de soporte más simple (por lo tanto, mejor mantenible).

Otra diferencia entre las 2 funciones es el primer nivel de asignación (los punteros de matriz) cuando ntoks es apagado. Ambos analizan todos los tokens disponibles, pero adoptan enfoques bastante diferentes. str_toksarray_alloc() utiliza alloc-ahead con un tamaño inicial de 16 (char-pointers), duplicándolo a pedido en el ciclo de análisis. str_toksarray_alloc2() hace un primer pase contando todos los tokens disponibles, luego asigna esa cantidad de punteros de caracteres solo una vez. Ese primer pase se realiza con una función de ayuda. str_toksfound() que utiliza las funciones estándar strpbrk() y strchr(). También proporciono el código fuente de esa función, más abajo.

Depende de usted decidir qué enfoque es mejor, según las necesidades de su proyecto. Siéntase libre de ajustar el código de cada función para cualquier enfoque y tomarlo desde allí.

Diría que, en promedio y para cadenas realmente grandes, alloc-ahead es mucho más rápido, especialmente cuando el tamaño inicial y el factor de crecimiento se ajustan con precisión caso por caso (haciéndolos funcionar como parámetros, por ejemplo). Guardando ese pase extra con todos esos strchr()‘arena strpbrk()‘s puede hacer una diferencia allí. Sin embargo, con cadenas relativamente pequeñas, que es prácticamente la norma, asignar por adelantado solo un montón de punteros de caracteres es simplemente una exageración. No duele, pero desordena el código sin una buena razón en este caso. De todos modos, siéntete libre de elegir el que más te convenga.

Lo mismo ocurre con estas 2 funciones. diría que en la mayoría de los casos str_toksarray_alloc2() es mucho más fácil de manejar, ya que la memoria y el rendimiento rara vez son un problema con cadenas pequeñas o medianas. Si tiene que lidiar con cadenas enormes, considere usar str_toksarray_alloc() (aunque en esos casos, debe implementar una función de análisis de cadenas especializada, cercana a las necesidades de su proyecto y las especificaciones de su entrada).

Vaya, creo que eso fue un poco más que solo 2 centavos (lol).

De todos modos, aquí está el código de las 2 funciones y las auxiliares (he eliminado la mayoría de sus comentarios de descripción, ya que ya he cubierto casi todo).

Código fuente

str_toksarray_alloc():

// ----------------------------------------
// Tokenize destructively a nul-terminated source-string.
// Return a dynamically allocated, NULL terminated array of char-pointers
// each pointing to each token found in the source-string, or NULL on error.
//
char **str_toksarray_alloc(char **strp, const char *delim, size_t *ntoks, bool keepnulls)
{
    // sanity checks
    if ( !strp || !*strp || !**strp || !delim ) {
        goto failed;
    }

    char *strpSaved = *strp;                    // save initial *strp pointer
    bool ntoksOk = (ntoks && *ntoks);           // false when ntoks is muted
    size_t _ntoks = (ntoksOk ? *ntoks : 16);    // # of tokens to alloc-ahead

    // alloc array of char-pointers (+1 for NULL sentinel)
    char **toksarr = malloc( (_ntoks+1) * sizeof(*toksarr) );
    if ( !toksarr ) {
        goto failed;
    }

    // Parse *strp tokens into the array
    size_t i = 0;           // # of actually parsed tokens
    char *tok;
    while ( (tok = strsep(strp, delim)) ) {
        // if requested, ignore empty tokens
        if ( *tok == '\0' && !keepnulls ) {
            continue;
        }
        // non-muted ntoks reached? we are done
        if ( ntoksOk && i == _ntoks ) {
            *ntoks = i;
            break;
        }
        // muted ntoks & ran out of space? double toksarr and keep parsing
        if ( !ntoksOk && i == _ntoks ) {
            _ntoks *= 2;
            char **tmparr = realloc( toksarr, (_ntoks+1) * sizeof(*tmparr) );
            if ( !tmparr ) {
                *strp = strpSaved;
                free( toksarr );
                goto failed;
            }
            toksarr = tmparr;
        }
        toksarr[i++] = tok; // get token address
    }
    toksarr[i] = NULL;      // NULL sentinel

    *strp = strpSaved;      // restore initial *strp pointer
    if (ntoks) *ntoks = i;  // pass to caller # of parsed tokens
    return toksarr;

failed:
    if (ntoks) *ntoks = 0;
    return NULL;
}

str_toksarray_alloc2():

// ----------------------------------------
// Tokenize non-destructively a nul-terminated source-string.
// Return a dynamically allocated, NULL terminated array of dynamically
// allocated and nul-terminated string copies of each token found in the
// source-string. Return NULL on error.
// The 2 at the end of the name means 2-levels of allocation.
//
char **str_toksarray_alloc2( const char *str, const char *delim, size_t *ntoks, bool keepnulls )
{
    // sanity checks
    if ( !str || !*str || !delim ) {
        if (ntoks) *ntoks = 0;
        return NULL;
    }

    // make a copy of str to work with
    char *_str = strdup( str ); 
    if ( !_str ) {
        if (ntoks) *ntoks = 0;
        return NULL;
    }

    // if ntoks is muted we'll allocate str_tokscount() tokens, else *ntoks
    size_t _ntoks = (ntoks && *ntoks) ? *ntoks : str_tokscount(_str, delim, keepnulls);
    if ( _ntoks == 0 ) {        // str_tokscount() failed
        goto fail_free_str;
    }
    
    // alloc the array of strings (+1 for an extra NULL sentinel)
    char **toksarr = malloc( (_ntoks+1) * sizeof(*toksarr) );
    if ( !toksarr ) {
        goto fail_free_str;
    }

    // Parse str tokens and duplicate them into the array
    size_t i = 0;           // # of actually parsed tokens
    char *tok;
    while ( i < _ntoks && (tok = strsep(&_str, delim)) ) {
        // if requested, skip empty tokens
        if ( *tok == '\0' && !keepnulls ) {
            continue;
        }
        // duplicate current token into the array
        char *tmptok = strdup( tok );
        if ( !tmptok ) {
            goto fail_free_arr;
        }
        toksarr[i++] = tmptok;
    }
    toksarr[i] = NULL;      // NULL sentinel

    free( _str );           // release the local copy of the source-string
    if (ntoks) *ntoks = i;  // pass to caller the # of parsed tokens
    return toksarr;

// cleanup before failing
fail_free_arr:
    for (size_t idx=0; idx < i; idx++) {
        free( toksarr[idx] );
    }
    free( toksarr );

fail_free_str:
    free( _str );
    if (ntoks) *ntoks = 0;
    return NULL;
}

str_tokscount() – función auxiliar, utilizada por str_toksarr_alloc2():

// ----------------------------------------
// Return the count of tokens present in a nul-terminated source-string (str),
// based on the delimiting chars contained in a 2nd nul-terminated string (delim).
// If the boolean argument is false, empty tokens are excluded.
//
// To stay consistent with the behavior of strsep(), the function returns 1 if
// delim is an empty string or none of its delimiters is found in str (in those
// cases the source-string is considered a single token).
// 0 is returned when str or delim are passed as NULL pointers, or when str is
// passed as an empty string.
//
size_t str_tokscount( const char *str, const char *delim, bool keepnulls )
{
    // sanity checks
    if ( !str || !*str || !delim ) {
        return 0;
    }

    const char *tok = str;
    size_t nnulls = strchr(delim, *str) ? 1 : 0;
    size_t ntoks = 1;   // even when no delims in str, str counts as 1 token 
    for (; (str = strpbrk(tok, delim)); ntoks++ ) {
        tok = ++str;
        if ( strchr(delim, *str) ) {
            nnulls++;
        }
    }

    return keepnulls ? ntoks : (ntoks - nnulls);
}

toksarray_free2() – Úselo en la matriz devuelta por str_toksarr_alloc2():

// ----------------------------------------
// Free a dynamically allocated, NULL terminated, array of char-pointers
// with each such pointer pointing to its own dynamically allocated data.
// Return NULL, so the caller has the choice of assigning it back to the
// dangling pointer. The 2 at the end of the name means 2-levels of deallocation.
//
// NULL terminated array means ending with a NULL sentinel.
//      e.g.: toksarr[0] = tok1, ..., toksarr[len] = NULL
//
char **toksarray_free2( char **toksarr )
{
    if ( toksarr ) {
        char **toks = toksarr;
        while ( *toks ) {   // walk until NULL sentinel
            free( *toks++ );
        }
        free( toksarr );
    }

    return NULL;
}

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad