strtok()在C语言中如何将字符串分割成标记?

133
请解释一下strtok()函数的工作原理。手册上说它将字符串分解为标记,但我无法从手册中理解它实际上是做什么的。
我在str*pch上添加了监视器以检查第一个while循环发生时它的工作情况,当str的内容只有"this"时,下面显示在屏幕上的输出是如何产生的?
/* strtok example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] ="- This, a sample string.";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," ,.-");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
  }
  return 0;
}

输出:

将字符串“- This,a sample string。”拆分为标记:
This
a
sample
string

5
strtok() 在返回结果之前,会修改输入的字符串并用空字符(NUL)作为分隔符。如果您试图在连续调用strtok()之间检查整个缓冲区(即str[]),您会发现它已经被修改了。 - Michael Foukarakis
3
说实话,我从来没去核实过,但我想它会存储最后传入的指针以及离开的位置。那么如果指针为空,程序就可以继续执行;如果不为空,程序将清除位置并重新开始。 - chris
基本上,如果你传递NULL,它会保存最后一个返回结果并从下一个字符继续搜索。这显然使它不是线程安全的,一次只能有一个标记化活动。参考 - DCoder
这是一个闭包吗?我不知道在C语言中如何让函数存储状态。 - Hanfei Sun
7
@Firegun:静态变量 - DCoder
显示剩余5条评论
16个回答

2

strtok()函数会将指针存储在静态变量中,以便在下次调用时从上一次离开的位置继续处理。因此,在第二次调用时,当我们传递null参数时,strtok()将从静态变量中获取指针。

如果您提供相同的字符串名称,它将再次从开头开始处理。

此外,strtok()是破坏性的,即它会更改原始字符串。因此,请确保始终有原始字符串的副本。

使用strtok()的另一个问题是,由于它将地址存储在静态变量中,在多线程编程中调用strtok()多次将导致错误。为此,请使用strtok_r()。


2

这是我使用哈希表作为分隔符实现的代码,这意味着它的时间复杂度是O(n),而不是O(n^2)。(这里是代码链接)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define DICT_LEN 256

int *create_delim_dict(char *delim)
{
    int *d = (int*)malloc(sizeof(int)*DICT_LEN);
    memset((void*)d, 0, sizeof(int)*DICT_LEN);

    int i;
    for(i=0; i< strlen(delim); i++) {
        d[delim[i]] = 1;
    }
    return d;
}



char *my_strtok(char *str, char *delim)
{

    static char *last, *to_free;
    int *deli_dict = create_delim_dict(delim);

    if(!deli_dict) {
        /*this check if we allocate and fail the second time with entering this function */
        if(to_free) {
            free(to_free);
        }
        return NULL;
    }

    if(str) {
        last = (char*)malloc(strlen(str)+1);
        if(!last) {
            free(deli_dict);
            return NULL;
        }
        to_free = last;
        strcpy(last, str);
    }

    while(deli_dict[*last] && *last != '\0') {
        last++;
    }
    str = last;
    if(*last == '\0') {
        free(deli_dict);
        free(to_free);
        deli_dict = NULL;
        to_free = NULL;
        return NULL;
    }
    while (*last != '\0' && !deli_dict[*last]) {
        last++;
    }

    *last = '\0';
    last++;

    free(deli_dict);
    return str;
}

int main()
{
    char * str = "- This, a sample string.";
    char *del = " ,.-";
    char *s = my_strtok(str, del);
    while(s) {
        printf("%s\n", s);
        s = my_strtok(NULL, del);
    }
    return 0;
}

1

0

strtok函数将给定字符串中的分隔符替换为'\0'空字符。

代码

#include<iostream>
#include<cstring>

int main()
{
    char s[]="30/4/2021";     
    std::cout<<(void*)s<<"\n";    // 0x70fdf0
    
    char *p1=(char*)0x70fdf0;
    std::cout<<p1<<"\n";
    
    char *p2=strtok(s,"/");
    std::cout<<(void*)p2<<"\n";
    std::cout<<p2<<"\n";
    
    char *p3=(char*)0x70fdf0;
    std::cout<<p3<<"\n";
    
    for(int i=0;i<=9;i++)
    {
        std::cout<<*p1;
        p1++;
    }
    
}

输出

0x70fdf0       // 1. address of string s
30/4/2021      // 2. print string s through ptr p1 
0x70fdf0       // 3. this address is return by strtok to ptr p2
30             // 4. print string which pointed by p2
30             // 5. again assign address of string s to ptr p3 try to print string
30 4/2021      // 6. print characters of string s one by one using loop

在字符串分词之前

我把字符串s的地址指派给了某个指针p1,并尝试通过该指针打印字符串,整个字符串都被打印出来。

分词之后

strtok将字符串s的地址返回给了指针p2,但是当我尝试通过指针打印字符串时,它只打印了“30”,没有打印整个字符串。因此可以确定strtok不仅仅是返回地址,而且在定界符处放置了'\0'字符

交叉检查

1.

接着我再次将字符串s的地址指派给某个指针p3,并尝试打印字符串,由于在分词过程中字符串已经更新为在定界符处添加了'\0',所以它打印了“30”。

2.

透过循环一个字符一个字符地输出字符串s,第一个定界符被替换成了'\0',所以它打印的是空格而不是''。


0
你可以扫描字符数组以查找令牌,如果找到它,就打印新行,否则打印该字符。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char *s;
    s = malloc(1024 * sizeof(char));
    scanf("%[^\n]", s);
    s = realloc(s, strlen(s) + 1);
    int len = strlen(s);
    char delim =' ';
    for(int i = 0; i < len; i++) {
        if(s[i] == delim) {
            printf("\n");
        }
        else {
            printf("%c", s[i]);
        }
    }
    free(s);
    return 0;
}

0

嘿,这是一段代码片段,可以帮助更好地理解这个主题。

打印Tokens

任务:给定一个句子s,将句子中的每个单词打印在新行中。

char *s;
s = malloc(1024 * sizeof(char));
scanf("%[^\n]", s);
s = realloc(s, strlen(s) + 1);
//logic to print the tokens of the sentence.
for (char *p = strtok(s," "); p != NULL; p = strtok(NULL, " "))
{
    printf("%s\n",p);
}

输入: 这是怎么回事

结果:

How
is
that
解释: 这里使用了 "strtok()" 函数,并使用 for 循环迭代以在单独的行中打印标记。
该函数将以 'string' 和 'break-point' 作为参数,将字符串在这些断点处分割并形成标记。现在,这些标记存储在 'p' 中,并用于进一步打印。

我认为通过举例说明比参考文档更好。 - tr_abhishek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接