1. 程式人生 > >strtok函式原始碼

strtok函式原始碼

今天用到strtok時,總感覺怪怪的,為啥第二次呼叫第一個引數要用NULL, 難道是函式內部儲存了當前的狀態,假如這樣的話,那就不能對多個串交叉呼叫strtok了,而且儲存這個狀態的不是全域性變數就是static變數。 於是看了他的原始碼,有幾個不同的版本,思想是一樣的。 感覺寫的挺巧妙的,深深的折服這些寫庫函式的牛人了。 下面是原始碼

版本一

    char *s;            /* string to search for tokens */
    const char *delim;  /* delimiting characters */
{
    static char *lasts;
    register int ch;

    if (s == 0)
	s = lasts;
    do {
	if ((ch = *s++) == '\0')
	    return 0;
    } while (strchr(delim, ch));
    --s;
    lasts = s + strcspn(s, delim);
    if (*lasts != 0)
	*lasts++ = 0;
    return s;
}

上面用到了strcspn函式,這個函式返回值是n,表示s的前n個字元都沒有在delim中出現。原理是這樣的,函式在一開始判斷第一個引數是不是空,假如是的話,也就是第二次呼叫這個函數了,那麼把上一次的狀態last賦值給要返回的串,然後那個do實現的功能其實就是strspn函式,這個函式的返回值是n,代表 s的前n個字元全部在delim出現。也就是說do實現的是跳過s中字元屬於分隔符的字元。 然後通過下面的strcspn真正擷取需要的字元數。所以last就指向了下一個分隔符或者字元末尾。然後判斷last是否是末尾,假如不是的話,把當前的字元也就是分隔符標記成0,其實就是把前一段字元截斷了。last指向了下一個字元。 然後返回s。

版本二 把do直接用 strspn函式代替,思想一樣的

#include <string.h> /* strspn() strcspn() */
char *strtok(char * str, const char * delim)
{
    static char* p=0;
    if(str)
        p=str;
    else if(!p)
        return 0;
    str=p+strspn(p,delim);
    p=str+strcspn(str,delim);
    if(p==str)
        return p=0;
    p = *p ? *p=0,p+1 : 0;
    return str;
}


第三個版本直接是 沒有使用任何函式,用的是雜湊實現,這個雜湊的妙處真是沒誰了,這才是真正c語言程式好不好

char*  strtok_r(char* string_org,const char* demial) {
	static unsigned char* last; 
	unsigned char* str;         
	const unsigned char* ctrl = (const unsigned char*)demial;
	unsigned char map[32]; 
	int count;
	
	for (count =0; count <32; count++){
		map[count] = 0;
	}   
	do {
		map[*ctrl >> 3] |= (1 << (*ctrl & 7));
	} while (*ctrl++);     
	if (string_org){
		str = (unsigned char*)string_org;
	} else{
		str = last;
	}
	while ((map[*str >> 3] & (1 << (*str & 7)))  && *str){
		str++;
	} 
	string_org = (char*)str; 
	for (;*str; str++){
		if ( map[*str >> 3] & (1 << (*str & 7))){
			*str++ = '\0';
			break;         
		}         
	}    
	last =str;    
	if (string_org == (char*)str){
		return NULL; 
	}else{
		return string_org;
	}
}