Tokenisation strtok() strtok r() 和 strtok s()
函数 strtok
使用一组分隔符将字符串分解为较小的字符串或标记。
#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" */
}
输出:
1: [Hello]
2: [world]
分隔符字符串可以包含一个或多个分隔符,并且每次调用 strtok
时可以使用不同的分隔符字符串。
调用 strtok
继续标记相同的源字符串不应再次传递源字符串,而是将 NULL
作为第一个参数传递。如果传递相同的源字符串*,*则第一个令牌将被重新标记化。也就是说,给定相同的分隔符,strtok
将再次返回第一个令牌。
请注意,由于 strtok
不为标记分配新内存,因此会修改源字符串。也就是说,在上面的例子中,字符串 src
将被操作以产生由调用 strtok
返回的指针引用的标记。这意味着源字符串不能是 const
(因此它不能是字符串文字)。它还意味着分隔字节的标识丢失(即在示例中,“,”和“!”被有效地从源字符串中删除,你无法分辨哪个分隔符匹配)。
另请注意,源字符串中的多个连续分隔符被视为一个; 在示例中,第二个逗号被忽略。
strtok
既不是线程安全也不是重入,因为它在解析时使用静态缓冲区。这意味着如果一个函数调用 strtok
,它在使用 strtok
时没有调用的函数也可以使用 strtok
,并且任何本身使用 strtok
的函数都无法调用它。
举例说明 strtok
不可重入的问题如下:
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);
输出:
[1.2]
[1]
[2]
预期的操作是外部 do while
循环应该创建由每个十进制数字字符串(1.2
,3.5
,4.2
)组成的三个标记,对于每个标记,strtok
调用内部循环应该将它分成单独的数字字符串(1
,2
,3
,5
,4
,2
)。
但是,因为 strtok
不是可重入的,所以不会发生这种情况。相反,第一个 strtok
正确地创建了“1.2 \ 0”标记,并且内部循环正确地创建了标记 1
和 2
。但是外循环中的 strtok
位于内循环使用的字符串的末尾,并立即返回 NULL。src
阵列的第二个和第三个子字符串根本没有被分析。
Version < C11
标准 C 库不包含线程安全或可重入版本,但其他一些版本不包含,例如 POSIX’strtok_r
。请注意,在 MSVC 上,strtok
等效,strtok_s
是线程安全的。
Version >= C11
C11 有一个可选部分,附件 K,提供了一个名为 strtok_s
的线程安全和可重入版本。你可以使用 __STDC_LIB_EXT1__
测试该功能。此可选部分未得到广泛支持。
strtok_s
函数与 POSIX strtok_r
函数的不同之处在于防止在被标记化的字符串之外存储,并通过检查运行时约束。但是,在正确编写的程序中,strtok_s
和 strtok_r
的行为相同。
现在使用 strtok_s
与示例产生正确的响应,如下所示:
/* 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);
输出将是:
[1.2]
[1]
[2]
[3.5]
[3]
[5]
[4.2]
[4]
[2]