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]