strtok()如何将string拆分为C中的标记?
请解释我的strtok()
函数的工作。手册说,它将string分解为令牌。 我无法从手册中了解它究竟做了什么。
我在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; }
输出:
分割string“ - 这是一个示例string”。 到令牌: 这个 一个 样品 串
strtok()将string分为标记。 即从任何一个分隔符开始到下一个将是您的一个令牌。 在你的情况下,起始标记将从“ – ”开始并以下一个空格“”结束。 然后下一个标记将从“”开始并以“,”结束。 在这里你得到“This”作为输出。 类似地,string的其余部分从空间到空间被拆分成令牌,最后结束“。”上的最后一个标记。
strtok运行时function就像这样工作
第一次调用strtok时,你提供了一个你想要标记的string
char s[] = "this is a string";
在上面的string空间似乎是一个很好的单词之间的分隔符,所以让我们使用:
char* p = strtok(s, " ");
现在发生的事情是,'''被search,直到find空格字符,第一个标记返回('this'),p指向该标记(string)
为了获得下一个标记并继续使用相同的string,NULL作为第一个parameter passing,因为strtok维护一个静态指针 , 指向上一个传入的string:
p = strtok(NULL," ");
现在p指向'是'
依此类推,直到找不到更多空格,最后一个string作为最后一个标记“string”返回。
更方便的,你可以这样写,而不是打印出所有的令牌:
for (char *p = strtok(s," "); p != NULL; p = strtok(NULL, " ")) { puts(p); }
编辑:
如果你想存储从strtok
返回的值,你需要将令牌复制到另一个缓冲区,例如strdup(p);
因为原始string(由strtok
内的静态指针指向)在迭代之间被修改以便返回令牌。
strtok
维护一个静态的内部引用,指向string中下一个可用的标记; 如果你传递一个NULL指针,它将从该内部引用工作。
这就是strtok
不可重入的原因; 只要你传递一个新的指针,旧的内部引用就会被破坏。
strtok
不会改变参数本身( str
)。 它存储该指针(在本地静态variables中)。 然后,可以在后续调用中更改该参数指向的内容,而不必传回参数。 (它可以提前保留指针,但是需要执行操作。)
从POSIX strtok
页面:
此函数使用静态存储来跟踪调用之间的当前string位置。
有一个线程安全的变体( strtok_r
)不会做这种types的魔法。
你第一次调用它,你提供了string来标记为strtok
。 然后,为了得到下面的标记,只要给它返回一个非NULL
指针即可。
strtok
函数logging您在调用时首次提供的string。 (这对于multithreading应用程序是非常危险的)
strtok会标记一个string,即将其转换为一系列的子string。
它通过search分隔这些令牌(或子string)的分隔符来实现。 而你指定的分隔符。 在你的情况下,你想要“或”,“或”。 或“ – ”作为分隔符。
提取这些令牌的编程模型是,您需要手工创build主string和分隔符集。 然后你重复地调用它,每次strtok都会返回find的下一个标记。 直到它到达主string的末尾,当它返回一个null。 另一个规则是只在第一次传递string,在后续时间传递NULL。 如果您正在使用新string开始新的标记化会话,或者正在从先前的标记化会话中检索标记,则可以告诉strtok。 请注意,strtok会记住它的标记化会话的状态。 由于这个原因,它不是可重入的或线程安全的(你应该使用strtok_r来代替)。 另外要知道的是,它实际上修改了原始string。 它为它发现的分隔符写入'\ 0'。
调用strtok的一个方法是,如下所示:
char str[] = "this, is the string - I want to parse"; char delim[] = " ,-"; char* token; for (token = strtok(str, delim); token; token = strtok(NULL, delim)) { printf("token=%s\n", token); }
结果:
this is the string I want to parse
strtok修改它的inputstring。 它将空字符('\ 0')放在其中,以便它将原始string的位作为记号返回。 其实strtok不分配内存。 如果您将string绘制为一系列框,您可能会更好理解。
要理解strtok()
如何工作,首先需要知道静态variables是什么。 这个链接解释得很好….
strtok()
的操作的关键是保留最后一个分隔符在连续调用之间的位置(这就是为什么当strtok()
继续调用连续调用中的null pointer
时,会继续parsing传递给它的原始string) ..
看看我自己的strtok()
实现,名为zStrtok()
,它具有与strtok()
提供的function略有不同的function。
char *zStrtok(char *str, const char *delim) { static char *static_str=0; /* var to store last address */ int index=0, strlength=0; /* integers for indexes */ int found = 0; /* check if delim is found */ /* delimiter cannot be NULL * if no more char left, return NULL as well */ if (delim==0 || (str == 0 && static_str == 0)) return 0; if (str == 0) str = static_str; /* get length of string */ while(str[strlength]) strlength++; /* find the first occurance of delim */ for (index=0;index<strlength;index++) if (str[index]==delim[0]) { found=1; break; } /* if delim is not contained in str, return str */ if (!found) { static_str = 0; return str; } /* check for consecutive delimiters *if first char is delim, return delim */ if (str[0]==delim[0]) { static_str = (str + 1); return (char *)delim; } /* terminate the string * this assignmetn requires char[], so str has to * be char[] rather than *char */ str[index] = '\0'; /* save the rest of the string */ if ((str + index + 1)!=0) static_str = (str + index + 1); else static_str = 0; return str; }
这里是一个示例用法
Example Usage char str[] = "A,B,,,C"; printf("1 %s\n",zStrtok(s,",")); printf("2 %s\n",zStrtok(NULL,",")); printf("3 %s\n",zStrtok(NULL,",")); printf("4 %s\n",zStrtok(NULL,",")); printf("5 %s\n",zStrtok(NULL,",")); printf("6 %s\n",zStrtok(NULL,",")); Example Output 1 A 2 B 3 , 4 , 5 C 6 (null)
代码来自我在Github上维护的string处理库 ,名为zString。 看看代码,甚至贡献:) https://github.com/fnoyanisi/zString
strtok用NULLreplace第二个参数中的字符,NULL字符也是string的结尾。
这里是我的实现,它使用哈希表作为分隔符,这意味着它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; }
这是我如何实施strtok,不是很好,但工作2小时后,终于得到它的工作。 它支持多个分隔符。
#include "stdafx.h" #include <iostream> using namespace std; char* mystrtok(char str[],char filter[]) { if(filter == NULL) { return str; } static char *ptr = str; static int flag = 0; if(flag == 1) { return NULL; } char* ptrReturn = ptr; for(int j = 0; ptr != '\0'; j++) { for(int i=0 ; filter[i] != '\0' ; i++) { if(ptr[j] == '\0') { flag = 1; return ptrReturn; } if( ptr[j] == filter[i]) { ptr[j] = '\0'; ptr+=j+1; return ptrReturn; } } } return NULL; } int _tmain(int argc, _TCHAR* argv[]) { char str[200] = "This,is my,string.test"; char *ppt = mystrtok(str,", ."); while(ppt != NULL ) { cout<< ppt << endl; ppt = mystrtok(NULL,", ."); } return 0; }
- 如何与C预处理器连接两次,并在“arg ## _ ## MACRO”中展开macros?
- 如何从Lucene TokenStream获取令牌?
- Google刷新令牌是否过期?
- 护照本地与node-jwt-simple
- 无法从Facebook获取访问令牌。 有一个OAuthException说:“错误validationvalidation码”
- Jenkins用户身份validation细节如何被“传递”给使用Jenkins API创build作业的脚本?
- validation从移动(iPhone)应用程序到ASP.Net Web API的请求(请在我的devise中提供反馈)
- 警告:在API开发的情况下,无法validationCSRF令牌的真实性
- Devise中的Token Authenticatable模块