1. 今天看了看strtok函數,特意找了下Linux內核2.0.1版本的代碼,因為在更高版本(至少2.6)已經使用strsep替換了該函數.
函數原型:
char * strtok(char * s,const char * ct)
使用第二個參數ct中的分隔符字符串,分割第一個參數s,ct參數的分隔符可以是任意字符,可以是單個字符的分隔符,也可以是字符串形式的分隔符如:"!,;'/"等,都可以作為分隔符。例如:
s="abc,def,123;456!/aaa"
ct=",;!/"
s將被分割為為:abc def 123 456 aaa
測試代碼:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char s[] = "abc,def,123;456!/aaa"; char delim[] = " ,;!/"; char *p = NULL; for(p = strtok(s, delim); p != NULL; p = strtok(NULL, delim)) { printf("%s ", p); } printf("\n"); return 0; }[ljq@ycy algorithm]$ gcc strtok.c -o strtok[ljq@ycy algorithm]$ ./strtok
abc def 123 456 aaa
2. strtok代碼分析,以下代碼摘自Linux2.01.版本:
char * strtok(char * s,const char * ct) { char *sbegin, *send; sbegin = s ? s : ___strtok; if (!sbegin) { return NULL; } sbegin += strspn(sbegin,ct); if (*sbegin == '\0') { ___strtok = NULL; return( NULL ); } send = strpbrk( sbegin, ct); if (send && *send != '\0') *send++ = '\0'; ___strtok = send; return (sbegin); }先說下strtok的整體思想也就是關鍵的幾個步驟:
a。首先strtok將數據保存在全局變量__strtok中,因此不是線程安全的也即不可重入。
b。strtok查找分隔符字符串時,跳過連續的分隔符,這樣可以忽略連續分隔符之間的空串,連續分隔符是從源字符串開始位置計算,個數通過函數strspn計算得出。
size_t strspn(const char *s, const char *accept),計算字符串 str 中連續有幾個字符都屬於字符串 accept。
例如:";,/!ABC!/DEF",前四個字符都是分隔符且是連續的,那麼strspn函數返回4,有效數據跳過前4個分隔符就從字符A開始,直到下一分隔符。
c。調用函數strpbrk,即源字符串中的字符如果與分隔符字符串中任意字符相同,就返回指向源字符串中該字符的指針,即找到了分隔符,返回該數據的指針。
char * strpbrk(const char * cs,const char * ct),比較字符串str1和str2中是否有相同的字符,如果有,則返回該字符在str1中的位置的指針。
例如:";,/!ABC!/DEF",跳過4個分隔符後,從A開始直到遇到分隔符感歎號(!) 那麼,就返回指向數據起始位置的指針,該指針指向字符A。
細節分析:
a。 變量__strtok是全局變量,定義在string.c文件中:char * ___strtok = NULL; 在頭文件linux/string.h中進行外部聲明,只要使用該變量的c文件包含該頭文件即可;
b。全局變量__strtok保存了剩余未做分隔的字符串的起始地址,每次調用strtok函數,都從全局變量__strtok指向的地址開始查找參數中的分隔符字符串,找到之後__strtok指向本次分隔符的下一位置(有效數據或者結尾符\0,又或者是分隔符如果有連續分隔符的話)。
c。對於字符串 "abc,def,123;456!/aaa"; 第一次調用strtok之後,分隔符逗號(,)被設置為\0,__strtok設置為指向第一個分隔符逗號之後的數據即__strtok指向字符d,返回指向字符a的指針;第二次調用strtok時,從__strtok指向的字符d開始查找delim中的分隔符,找到第二個逗號時,與第一次操作一樣,分隔符逗號被設置為\0,並從新設置__strtok指向字符1,返回指向字符d的指針,後續一直如此循環。
d。如果源字符串包含連續分隔符,則調用函數strspn計算出連續相同的分隔符字符後會跳過這些分隔符。
附上函數strspn的測試示例:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int ret = 0; char s1[] = "abc,;!/}defgh,,,**#123,456"; char s2[] = ";,/#}*abc,;!/}defgh,,,**#123,456"; char delim[] = ",;!/}#*"; ret = strspn(s1, delim); printf("ret1:%d\n", ret); ret = strspn(s2, delim); printf("ret2:%d\n", ret); return 0; }結果如下:
[ljq@ycy algorithm]$ gcc strspn.c -o strspn
[ljq@ycy algorithm]$ ./strspn
ret1:0
ret2:6
函數是從源字符串開始位置匹配分隔符,只有開始的連續才起作用,開始有1個或者多個則返回匹配到的分隔符個數,如果開始位置一個分隔符都沒有,則返回0,如果有1個則返回1個,上例中返回6個。