[英]Split a String in Standard C without strtok() and strsep()?
[英]What are the differences between strtok and strsep in C
有人能解釋一下strtok()
和strsep()
之間有什么區別嗎? 它們的優點和缺點是什么? 為什么我會選擇一個而不是另一個。
strtok()
和strsep()
之間的一個主要區別是strtok()
是標准化的(通過 C 標准,因此也通過 POSIX)但strsep()
沒有標准化(通過 C 或 POSIX;它在 GNU C 中可用庫,起源於 BSD)。 因此,可移植代碼更可能使用strtok()
不是strsep()
。
另一個區別是對不同字符串的strsep()
函數的調用可以交錯,而使用strtok()
則不能這樣做(盡管使用strtok_r()
)。 因此,在庫中使用strsep()
不會意外破壞其他代碼,而在庫函數中使用strtok()
必須記錄在案,因為同時使用strtok()
其他代碼無法調用庫函數。
kernel.org 的strsep()
手冊頁說:
strsep() 函數被引入作為 strtok(3) 的替代品,因為后者不能處理空字段。
因此,另一個主要區別是George Gaál在他的回答中強調的一個區別; strtok()
允許單個標記之間有多個分隔符,而strsep()
期望標記之間有一個分隔符,並將相鄰的分隔符解釋為空標記。
strsep()
和strtok()
修改了它們的輸入字符串,並且都strsep()
您識別標記了標記結尾的分隔符(因為它們都在標記結尾之后的分隔符上寫了一個 NUL '\\0'
)。
strsep()
。strtok_r()
時,您可以使用strtok_r()
)。strtok()
。 而且你只能使用它足夠長的時間來讓你擺脫危及生命的境地; 然后,您將再次放棄對它的所有使用。 它有毒; 不要使用它。 編寫自己的strtok_r()
或strsep()
比使用strtok()
更好。strtok()
有毒? 如果在庫函數中使用strtok()
函數是有毒的。 如果您的庫函數使用strtok()
,則必須清楚地記錄下來。
那是因為:
strtok()
並且調用您的函數也使用strtok()
,則會破壞調用函數。strtok()
的函數,這將破壞您的函數對strtok()
的使用。strtok()
— 跨一系列strtok()
調用。 這個問題的根源是調用之間保存的狀態,它允許strtok()
從它停止的地方繼續。 除了“不要使用strtok()
”之外,沒有其他明智的方法來解決這個問題。
strsep()
。strtok_r()
。strtok_s()
。strtok_s()
,但其接口與strtok_r()
和 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);
微軟strtok_s()
:
char *strtok_s(char *strToken, const char *strDelimit, char **context);
附件 K strtok_s()
:
char *strtok_s(char * restrict s1, rsize_t * restrict s1max,
const char * restrict s2, char ** restrict ptr);
請注意,這有 4 個參數,而不是strtok()
上的其他兩個變體中的 3 個參數。
來自 GNU C 庫手冊 - 在字符串中查找標記:
strsep
和strtok_r
之間的一個區別是,如果輸入字符串在一行中包含來自分隔符的多個字符,則strsep
為來自分隔符的每一對字符返回一個空字符串。 這意味着程序通常應該在處理之前測試strsep
返回空字符串。
strtok()
和strsep()
第一個區別是它們處理輸入字符串中連續分隔符的方式。
strtok()
處理的連續分隔符:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
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: %s\n", ptr);
token = strtok (ptr, delims);
while (token != NULL) {
printf("%s\n", token);
token = strtok (NULL, delims);
}
printf ("Original String: %s\n", ptr);
free (ptr);
return 0;
}
輸出:
# ./example1_strtok
Original String: aaa-bbb --ccc-ddd
aaa
bbb
ccc
ddd
Original String: aaa
在輸出中,您可以看到一個又一個的標記"bbb"
和"ccc"
。 strtok()
不指示連續分隔符的出現。 此外, strtok()
修改輸入字符串。
由strsep()
處理的連續分隔符字符:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
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: %s\n", ptr);
while ((token = strsep(&ptr1, delims)) != NULL) {
if (*token == '\0') {
token = "<empty>";
}
printf("%s\n", token);
}
if (ptr1 == NULL) // This is just to show that the strsep() modifies the pointer passed to it
printf ("ptr1 is NULL\n");
printf ("Original String: %s\n", ptr);
free (ptr);
return 0;
}
輸出:
# ./example1_strsep
Original String: aaa-bbb --ccc-ddd
aaa
bbb
<empty> <==============
<empty> <==============
ccc
ddd
ptr1 is NULL
Original String: aaa
在輸出中,您可以看到bbb
和ccc
之間的兩個空字符串(通過<empty>
表示)。 這兩個空字符串用於"--"
介於"bbb"
和"ccc"
。 當strsep()
在"bbb"
之后發現分隔符' '
,它將分隔符替換為'\\0'
字符並返回"bbb"
。 在此之后, strsep()
找到了另一個分隔符'-'
。 然后它將分隔符替換為'\\0'
字符並返回空字符串。 下一個分隔符也是如此。
當strsep()
返回指向空字符(即,值為'\\0'
的字符strsep()
的指針時,將指示連續的分隔符字符。
strsep()
修改輸入字符串以及其地址作為第一個參數傳遞給strsep()
的指針。
第二個區別是, strtok()
依賴於一個靜態變量來跟蹤字符串中的當前解析位置。 此實現需要在開始第二個字符串之前完全解析一個字符串。 但strsep()
並非如此。
當另一個strtok()
未完成時調用strtok()
:
#include <stdio.h>
#include <string.h>
void another_function_callng_strtok(void)
{
char str[] ="ttt -vvvv";
char* delims = " -";
char* token;
printf ("Original String: %s\n", str);
token = strtok (str, delims);
while (token != NULL) {
printf ("%s\n", 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: %s\n", str);
token = strtok (str, delims);
while (token != NULL)
{
printf ("%s\n",token);
another_function_callng_strtok();
token = strtok (NULL, delims);
}
}
int main(void) {
function_callng_strtok();
return 0;
}
輸出:
# ./example2_strtok
Original String: aaa --bbb-ccc
aaa
Original String: ttt -vvvv
ttt
vvvv
another_function_callng_strtok: I am done.
該功能function_callng_strtok()
只打印令牌"aaa"
,不打印輸入字符串的令牌的休息,因為它調用another_function_callng_strtok()
這又調用strtok()
並將其設置的靜態指針strtok()
以NULL
時完成提取所有令牌。 控制回來function_callng_strtok()
while
循環, strtok()
返回NULL
由於靜態指針指向NULL
並使得循環條件false
和退出循環。
當另一個strsep()
未完成時調用strsep()
:
#include <stdio.h>
#include <string.h>
void another_function_callng_strsep(void)
{
char str[] ="ttt -vvvv";
const char* delims = " -";
char* token;
char* ptr = str;
printf ("Original String: %s\n", str);
while ((token = strsep(&ptr, delims)) != NULL) {
if (*token == '\0') {
token = "<empty>";
}
printf("%s\n", 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: %s\n", str);
while ((token = strsep(&ptr, delims)) != NULL) {
if (*token == '\0') {
token = "<empty>";
}
printf("%s\n", token);
another_function_callng_strsep();
}
}
int main(void) {
function_callng_strsep();
return 0;
}
輸出:
# ./example2_strsep
Original String: aaa --bbb-ccc
aaa
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
<empty>
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
<empty>
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
bbb
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
ccc
Original String: ttt -vvvv
ttt
<empty>
vvvv
another_function_callng_strsep: I am done.
在這里您可以看到,在完全解析一個字符串之前調用strsep()
沒有任何區別。
因此, strtok()
和strsep()
的缺點是都修改輸入字符串,但strsep()
比strtok()
有幾個優點,如上所示。
從strsep :
strsep() 函數旨在替代 strtok() 函數。 雖然 strtok() 函數出於可移植性的原因應該是首選(它符合 ISO/IEC 9899:1990 (``ISO C90'')),但它無法處理空字段,即檢測由兩個相鄰分隔符分隔的字段,或一次用於多個字符串。 strsep() 函數首次出現在 4.4BSD 中。
以供參考:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.