C中的strtok和strsep有什么区别

miz*_*uki 25 c strtok strsep

可能有人给我解释一下什么样的差异之间存在着strtok()strsep()?它们的优点和缺点是什么?为什么我会选择一个而不是另一个.

Jon*_*ler 44

之间的一个主要区别strtok(),并strsep()strtok()被标准化(C标准,并因此也通过POSIX),但strsep()不规范(由C或POSIX;它是GNU C库中可用的,和起源于BSD).因此,可移植的代码更容易使用strtok()strsep().

另一个区别是strsep()对不同字符串上的函数的调用可以是交错的,而你不能这样做strtok()(尽管你可以strtok_r()).因此,strsep()在库中使用不会意外破坏其他代码,而strtok()在库函数中使用必须记录,因为strtok()同时使用的其他代码不能调用库函数.

该手册strsep()kernel.org说:

引入strsep()函数作为strtok(3)的替代,因为后者无法处理空字段.

因此,另一个主要区别是GeorgeGaál在他的回答中强调的那个; strtok()允许在单个标记之间使用多个分隔符,而strsep()期望标记之间使用单个分隔符,并将相邻分隔符解释为空标记.

双方strsep()strtok()修改其输入字符串,既不让你识别哪些分隔符标志着令牌结束(因为这两个写一个NUL '\0'令牌结束后在分离器).

什么时候使用它们?

  • 您可以strsep()在需要空令牌时使用,而不是在令牌之间允许多个分隔符,并且在您不介意可移植性时使用.
  • 你会使用strtok_r(),当你想允许标记之间的多个分隔符,你不想空标记(和POSIX足够的便携式你).
  • strtok()如果你不这样做,你只会在有人威胁你的生命时使用.而你只能用它足够长的时间让你摆脱危及生命的境地; 然后你会再次放弃使用它.它有毒; 不要使用它.编写自己的strtok_r()或者strsep()使用它会更好strtok().

为什么strtok()有毒?

strtok()如果在库函数中使用,该函数是有毒的.如果您的库函数使用strtok(),则必须清楚地记录.

那是因为:

  1. 如果任何调用函数正在使用strtok()并调用也使用的函数strtok(),则会中断调用函数.
  2. 如果你的函数调用任何调用的函数strtok(),那将破坏你的函数的使用strtok().
  3. 如果您的程序是多线程的,那么strtok()在任何给定时间最多只能有一个线程使用- 跨越一系列strtok()调用.

此问题的根源是调用之间保存的状态,允许strtok()在停止的位置继续.除了"不要使用strtok()" 之外,没有合理的方法来解决问题.

  • strsep()如果可用,您可以使用.
  • 您可以使用POSIX(strtok_r()如果可用).
  • strtok_s()如果可用,您可以使用Microsoft .
  • 名义上,您可以使用ISO/IEC 9899:2011附件K.3.7.3.1功能strtok_s(),但其界面strtok_r()与微软的界面不同strtok_s().

BSD strsep():

char *strsep(char **stringp, const char *delim);
Run Code Online (Sandbox Code Playgroud)

POSIX strtok_r():

char *strtok_r(char *restrict s, const char *restrict sep, char **restrict state);
Run Code Online (Sandbox Code Playgroud)

微软strtok_s():

char *strtok_s(char *strToken, const char *strDelimit, char **context);
Run Code Online (Sandbox Code Playgroud)

附件K strtok_s():

char *strtok_s(char * restrict s1, rsize_t * restrict s1max,
               const char * restrict s2, char ** restrict ptr);
Run Code Online (Sandbox Code Playgroud)

请注意,这有4个参数,而不是其他两个变体中的3个参数strtok().

  • 请注意,附件 K `strtok_s()` 声明为:`char *strtok_s(char * restrict s1, rsize_t * restrict s1max, const char * restrict s2, char ** restrict ptr);` 与接口不匹配Microsoft 的 `strtok_s()` 或 POSIX 的 `strtok_r()`。即使实现了,差异也很烦人——它限制了附件 K 功能的有用性。另请参阅 [您使用 TR 24731 '安全' 功能吗?](/sf/ask/26108631/) (2认同)

Geo*_*aál 8

从GNU C Library手册 - 在字符串中查找标记:

strsep和之间的一个区别strtok_r是,如果输入字符串在一行中包含多个来自分隔符的字符,strsep则会从分隔符返回每对字符的空字符串.这意味着程序通常应该strsep在处理之前测试返回空字符串.

  • 你可以找到使用这些函数的例子,如果你点击[链接]上(http://www.gnu.org/s/hello/manual/libc/Finding-Tokens-in-a-String.html):-)还请注意,C编译器中可能缺少`strsep`函数. (2认同)

H.S*_*.S. 6

在第一差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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

# ./example1_strtok
Original String: aaa-bbb --ccc-ddd
aaa
bbb
ccc
ddd
Original String: aaa
Run Code Online (Sandbox Code Playgroud)

在输出中,你可以看到令牌"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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

# ./example1_strsep
Original String: aaa-bbb --ccc-ddd
aaa
bbb
<empty>             <==============
<empty>             <==============
ccc
ddd
ptr1 is NULL
Original String: aaa
Run Code Online (Sandbox Code Playgroud)

在输出中,你可以看到两个空字符串(指示通过<empty>之间)bbbccc。这两个空字符串用于"--"between"bbb""ccc"。当strsep()找到一个分隔符' '"bbb",它替换分隔符'\0'字符并返回"bbb"。在此之后,strsep()找到了另一个分隔符'-'。然后它用字符替换分隔符'\0'并返回空字符串。下一个分隔符也是如此。

strsep()返回指向空字符(即值为 的字符'\0'的指针时,指示连续的分隔符字符

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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

# ./example2_strtok
Original String: aaa --bbb-ccc
aaa
Original String: ttt -vvvv
ttt
vvvv
another_function_callng_strtok: I am done.
Run Code Online (Sandbox Code Playgroud)

该函数function_callng_strtok()只打印标记"aaa",不打印输入字符串的其余标记,因为它会调用another_function_callng_strtok()它,然后调用strtok()它,strtok()NULL在完成提取所有标记时设置 的静态指针。控制返回function_callng_strtok() while循环,由于指向的静态指针strtok()返回,这使得循环条件和循环退出。NULLNULLfalse

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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

# ./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.
Run Code Online (Sandbox Code Playgroud)

在这里你可以看到,strsep()在完全解析一个字符串之前调用没有任何区别。

因此,的缺点strtok(),并strsep()为这两个修改输入字符串,但strsep()拥有几个优点strtok()之上,如图所示。

strsep

strsep() 函数旨在替代 strtok() 函数。虽然 strtok() 函数出于可移植性的原因应该是首选(它符合 ISO/IEC 9899:1990 (``ISO C90'')),但它无法处理空字段,即检测由两个相邻分隔符分隔的字段,或一次用于多个字符串。strsep() 函数首次出现在 4.4BSD 中。


以供参考: