Saltar al contenido

¿Cuáles son las diferencias entre strtok y strsep en C?

Solución:

Una gran diferencia entre strtok() y strsep() es eso strtok() está estandarizado (por el estándar C, y por lo tanto también por POSIX) pero strsep() no está estandarizado (por C o POSIX; está disponible en la biblioteca GNU C y se originó en BSD). Por lo tanto, es más probable que el código portátil utilice strtok() que strsep().

Otra diferencia es que las llamadas al strsep() La función en diferentes cadenas se puede intercalar, mientras que no se puede hacer eso con strtok() (aunque puedes con strtok_r()). Entonces, usando strsep() en una biblioteca no rompe otro código accidentalmente, mientras que el uso strtok() en una función de biblioteca debe documentarse porque otro código que usa strtok() al mismo tiempo, no puede llamar a la función de biblioteca.

La página de manual para strsep() en kernel.org dice:

La función strsep () se introdujo como reemplazo de strtok (3), ya que este último no puede manejar campos vacíos.

Así, la otra gran diferencia es la que destaca George Gaál en su respuesta; strtok() permite múltiples delimitadores entre un solo token, mientras que strsep() espera un solo delimitador entre tokens e interpreta los delimitadores adyacentes como un token vacío.

Ambos strsep() y strtok() modificar sus cadenas de entrada y ninguno le permite identificar qué carácter delimitador marcó el final del token (porque ambos escriben un NUL '' sobre el separador después del final del token).

¿Cuándo usarlos?

  • Usarías strsep() cuando desea tokens vacíos en lugar de permitir múltiples delimitadores entre tokens, y cuando no le importa la portabilidad.
  • Usarías strtok_r() cuando desea permitir múltiples delimitadores entre tokens y no desea tokens vacíos (y POSIX es lo suficientemente portátil para usted).
  • Solo usarías strtok() cuando alguien amenaza tu vida si no lo haces. Y solo lo usaría el tiempo suficiente para salir de la situación que amenaza su vida; entonces dejaría de usarlo una vez más. Es venenoso; No lo uses. Sería mejor que escribieras el tuyo strtok_r() o strsep() que usar strtok().

Por que es strtok() ¿venenoso?

los strtok() La función es venenosa si se usa en una función de biblioteca. Si su función de biblioteca usa strtok(), debe documentarse claramente.

Eso es porque:

  1. Si alguna función de llamada está usando strtok() y llama a tu función que también usa strtok(), rompes la función de llamada.
  2. Si su función llama a cualquier función que llame strtok(), eso romperá el uso de su función de strtok().
  3. Si su programa es multiproceso, como máximo se puede usar un hilo strtok() en un momento dado, a través de una secuencia de strtok() llamadas.

La raíz de este problema es el estado guardado entre llamadas que permite strtok() para continuar donde lo dejó. No existe una forma sensata de solucionar el problema que no sea “no usar strtok()“.

  • Puedes usar strsep() si está disponible.
  • Puedes usar POSIX’s strtok_r() si está disponible.
  • Puede utilizar Microsoft strtok_s() si está disponible.
  • Nominalmente, podría utilizar la función ISO / IEC 9899: 2011 Anexo K.3.7.3.1 strtok_s(), pero su interfaz es diferente de ambas strtok_r() y de Microsoft strtok_s().

BSD strsep():

char *strsep(char **stringp, const char *delim);

POSIX strtok_r():

char *strtok_r(char *restrict s, const char *restrict sep, char **restrict state);

Microsoft strtok_s():

char *strtok_s(char *strToken, const char *strDelimit, char **context);

Anexo K strtok_s():

char *strtok_s(char * restrict s1, rsize_t * restrict s1max,
               const char * restrict s2, char ** restrict ptr);

Tenga en cuenta que esto tiene 4 argumentos, no 3 como en las otras dos variantes en strtok().

Del manual de la biblioteca GNU C – Encontrar tokens en una cadena:

Una diferencia entre strsep y strtok_r es que si la entrada string contiene más de un carácter del delimitador en una fila strsep devuelve un vacío string para cada par de caracteres del delimitador. Esto significa que un programa normalmente debería probar strsep devolviendo un vacío string antes de procesarlo.

Primera diferencia en strtok() y strsep() es la forma en que manejan los caracteres delimitadores contiguos en la entrada string.

Caracteres delimitadores contiguos manejados por strtok():

#include 
#include 
#include 

int main(void) 
    const char* teststr = "aaa-bbb --ccc-ddd"; //Contiguous delimiters between bbb and ccc sub-string
    const char* delims = " -";  // delimiters - space and hyphen character
    char* token;
    char* ptr = strdup(teststr);

    if (ptr == NULL) 
        fprintf(stderr, "strdup failed");
        exit(EXIT_FAILURE);
    

    printf ("Original String: %sn", ptr);

    token = strtok (ptr, delims);
    while (token != NULL) 
        printf("%sn", token);
        token = strtok (NULL, delims);
    

    printf ("Original String: %sn", ptr);
    free (ptr);
    return 0;

Producción:

# ./example1_strtok
Original String: aaa-bbb --ccc-ddd
aaa
bbb
ccc
ddd
Original String: aaa

En la salida, puedes ver el token. "bbb" y "ccc" Uno después del otro. strtok()no indica la aparición de caracteres delimitadores contiguos. También el strtok()modificar la entrada string.

Caracteres delimitadores contiguos manejados por strsep():

#include 
#include 
#include 

int main(void) 
    const char* teststr = "aaa-bbb --ccc-ddd"; //Contiguous delimiters between bbb and ccc sub-string
    const char* delims = " -";  // delimiters - space and hyphen character
    char* token;
    char* ptr1;
    char* ptr = strdup(teststr);

    if (ptr == NULL) 
        fprintf(stderr, "strdup failed");
        exit(EXIT_FAILURE);
    

    ptr1 = ptr;

    printf ("Original String: %sn", ptr);
    while ((token = strsep(&ptr1, delims)) != NULL) 
        if (*token == '') 
            token = "";
        
        printf("%sn", token);
    

    if (ptr1 == NULL) // This is just to show that the strsep() modifies the pointer passed to it
        printf ("ptr1 is NULLn");
    printf ("Original String: %sn", ptr);
    free (ptr);
    return 0;

Producción:

# ./example1_strsep
Original String: aaa-bbb --ccc-ddd
aaa
bbb
             <==============
             <==============
ccc
ddd
ptr1 is NULL
Original String: aaa

En la salida, puedes ver los dos vacíos. string (indicado a través de ) Entre bbb y ccc. Esas dos cadenas vacías son para "--" Entre "bbb" y "ccc". Cuando strsep() encontró un carácter delimitador ' ' después "bbb", reemplazó el carácter delimitador con '' personaje y regresó "bbb". Después de este, strsep() encontró otro carácter delimitador '-'. Luego reemplazó el carácter delimitador con '' personaje y devolvió el vacío string. Lo mismo ocurre con el siguiente carácter delimitador.

Los caracteres delimitadores contiguos se indican cuando strsep() devuelve un puntero a un null personaje (es decir, un personaje con el valor '').

los strsep()modificar la entrada string así como el puntero cuya dirección pasó como primer argumento a strsep().

La segunda diferencia es, strtok() se basa en un static variable para realizar un seguimiento de la ubicación de análisis actual dentro de una string. Esta implementación requiere analizar completamente uno string antes de comenzar un segundo string. Pero este no es el caso con strsep().

Vocación strtok() cuando otro strtok() no está terminado:

#include 
#include 

void another_function_callng_strtok(void)

    char str[] ="ttt -vvvv";
    char* delims = " -";
    char* token;

    printf ("Original String: %sn", str);
    token = strtok (str, delims);
    while (token != NULL) 
        printf ("%sn", token);
        token = strtok (NULL, delims);
    
    printf ("another_function_callng_strtok: I am done.n");


void function_callng_strtok ()

    char str[] ="aaa --bbb-ccc";
    char* delims = " -";
    char* token;

    printf ("Original String: %sn", str);
    token = strtok (str, delims);
    while (token != NULL)
    
        printf ("%sn",token);
        another_function_callng_strtok();
        token = strtok (NULL, delims);
    


int main(void) 
    function_callng_strtok();
    return 0;

Producción:

# ./example2_strtok
Original String: aaa --bbb-ccc
aaa
Original String: ttt -vvvv
ttt
vvvv
another_function_callng_strtok: I am done.

La función function_callng_strtok() solo imprimir token "aaa" y no imprime el resto de los tokens de entrada string porque llama another_function_callng_strtok() que a su vez llama strtok() y estableció el static puntero de strtok() para NULL cuando termina de extraer todos los tokens. El control vuelve a function_callng_strtok()while círculo, strtok() devoluciones NULL debido a la static puntero apuntando a NULL y que hacen que la condición de bucle false y salidas de bucle.

Vocación strsep() cuando otro strsep() no está terminado:

#include 
#include 

void another_function_callng_strsep(void)

    char str[] ="ttt -vvvv";
    const char* delims = " -";
    char* token;
    char* ptr = str;

    printf ("Original String: %sn", str);
    while ((token = strsep(&ptr, delims)) != NULL) 
        if (*token == '') 
            token = "";
        
        printf("%sn", token);
    
    printf ("another_function_callng_strsep: I am done.n");


void function_callng_strsep ()

    char str[] ="aaa --bbb-ccc";
    const char* delims = " -";
    char* token;
    char* ptr = str;

    printf ("Original String: %sn", str);
    while ((token = strsep(&ptr, delims)) != NULL) 
        if (*token == '') 
            token = "";
        
        printf("%sn", token);
        another_function_callng_strsep();
    


int main(void) 
    function_callng_strsep();
    return 0;

Producción:

# ./example2_strsep
Original String: aaa --bbb-ccc
aaa
Original String: ttt -vvvv
ttt

vvvv
another_function_callng_strsep: I am done.

Original String: ttt -vvvv
ttt

vvvv
another_function_callng_strsep: I am done.

Original String: ttt -vvvv
ttt

vvvv
another_function_callng_strsep: I am done.
bbb
Original String: ttt -vvvv
ttt

vvvv
another_function_callng_strsep: I am done.
ccc
Original String: ttt -vvvv
ttt

vvvv
another_function_callng_strsep: I am done.

Aquí puedes ver, llamando strsep() antes de analizar completamente uno string no hace ninguna diferencia.

Entonces, la desventaja de strtok() y strsep() es que ambos modifican la entrada string pero strsep() tiene un par de ventajas sobre strtok() como se ilustra arriba.

De strsep:

La función strsep () está pensada como un reemplazo de la función strtok (). Si bien la función strtok () debe preferirse por razones de portabilidad (cumple con ISO / IEC 9899: 1990 (`` ISO C90 '')), no puede manejar campos vacíos, es decir, detectar campos delimitados por dos caracteres delimitadores adyacentes, o para ser utilizado por más de una string a la vez. La función strsep () apareció por primera vez en 4.4BSD.


Para referencia:

  • strtok () y strtok_r ()
  • strsep ()

valoraciones y reseñas

Tienes la opción de añadir valor a nuestra información cooperando tu veteranía en las referencias.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *