![](/img/trans.png)
[英]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.