C Language Tokenización: strtok (), strtok_r () y strtok_s ()


Ejemplo

La función strtok rompe una cadena en cadenas o tokens más pequeños, utilizando un conjunto de delimitadores.

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

int main(void)
{
    int toknum = 0;
    char src[] = "Hello,, world!";
    const char delimiters[] = ", !";
    char *token = strtok(src, delimiters);
    while (token != NULL)
    {
        printf("%d: [%s]\n", ++toknum, token);
        token = strtok(NULL, delimiters);
    }
    /* source is now "Hello\0, world\0\0" */
}

Salida:

1: [Hello]
2: [world]

La cadena de delimitadores puede contener uno o más delimitadores y se pueden usar diferentes cadenas delimitadoras con cada llamada a strtok .

Las llamadas a strtok para continuar tokenizando la misma cadena fuente no deben pasar la cadena fuente nuevamente, sino que pasan NULL como el primer argumento. Si se pasa la misma cadena de origen, el primer token se volverá a tokenizar. Es decir, dados los mismos delimitadores, strtok simplemente devolvería el primer token nuevamente.

Tenga en cuenta que como strtok no asigna nueva memoria para los tokens, modifica la cadena de origen . Es decir, en el ejemplo anterior, la cadena src se manipulará para producir los tokens a los que hace referencia el puntero devuelto por las llamadas a strtok . Esto significa que la cadena de origen no puede ser const (por lo que no puede ser un literal de cadena). También significa que la identidad del byte delimitador se pierde (es decir, en el ejemplo, "," y "!" Se eliminan de la cadena fuente de manera efectiva y no se puede saber qué carácter delimitador coincide).

Tenga en cuenta también que varios delimitadores consecutivos en la cadena de origen se tratan como uno solo; en el ejemplo, la segunda coma se ignora.

strtok no es seguro para subprocesos ni re-entrante porque usa un búfer estático mientras analiza. Esto significa que si una función llama a strtok , ninguna función a la que llama mientras está usando strtok también puede usar strtok , y no puede ser llamada por ninguna función que esté usando strtok .

Un ejemplo que demuestra los problemas causados ​​por el hecho de que strtok no es re-entrante es el siguiente:

char src[] = "1.2,3.5,4.2";
char *first = strtok(src, ","); 

do 
{
    char *part;
    /* Nested calls to strtok do not work as desired */
    printf("[%s]\n", first);
    part = strtok(first, ".");
    while (part != NULL)
    {
        printf(" [%s]\n", part);
        part = strtok(NULL, ".");
    }
} while ((first = strtok(NULL, ",")) != NULL);

Salida:

[1.2]
 [1]
 [2]

La operación esperada es que el bucle do while externo debe crear tres tokens que constan de cada cadena de número decimal ( "1.2" , "3.5" , "4.2" ), para cada uno de los cuales strtok solicita el bucle interno debe dividirlo en partes separadas. cadenas de dígitos ( "1" , "2" , "3" , "5" , "4" , "2" ).

Sin embargo, debido a que strtok no es reingresante, esto no ocurre. En su lugar, el primer strtok crea correctamente el token "1.2 \ 0", y el bucle interno crea correctamente los tokens "1" y "2" . Pero entonces el strtok en el bucle externo está al final de la cadena utilizada por el bucle interno, y devuelve NULL inmediatamente. La segunda y tercera subcadenas de la matriz src no se analizan en absoluto.

C11

Las bibliotecas estándar de C no contienen una versión segura para subprocesos o de reingreso, pero otras sí, como el strtok_r POSIX. Tenga en cuenta que en el MSVC strtok equivalente, strtok_s es seguro para subprocesos.

C11

C11 tiene una parte opcional, el Anexo K, que ofrece una versión segura para subprocesos y re-entrant llamada strtok_s . Puede probar la función con __STDC_LIB_EXT1__ . Esta parte opcional no es ampliamente soportada.

La función strtok_s difiere de la función strtok_r POSIX al protegerse contra el almacenamiento fuera de la cadena que está siendo tokenizada, y al verificar las restricciones de tiempo de ejecución. Sin embargo, en programas escritos correctamente, los strtok_s y strtok_r comportan igual.

El uso de strtok_s con el ejemplo ahora produce la respuesta correcta, así:

/* you have to announce that you want to use Annex K */ 
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>

#ifndef __STDC_LIB_EXT1__
# error "we need strtok_s from Annex K"
#endif

char src[] = "1.2,3.5,4.2";  
char *next = NULL;
char *first = strtok_s(src, ",", &next);

do 
{
    char *part;
    char *posn;

    printf("[%s]\n", first);
    part = strtok_s(first, ".", &posn);
    while (part != NULL)
    {
        printf(" [%s]\n", part);
        part = strtok_s(NULL, ".", &posn);
    }
} 
while ((first = strtok_s(NULL, ",", &next)) != NULL);

Y la salida será:

[1.2]
 [1]
 [2]
[3.5]
 [3]
 [5]
[4.2]
 [4]
 [2]