Bootstrap

C库源代码实现: C/C++中的Split函数strtok

C库源代码实现: strtok

C/C++中的Split函数是strtok()其函数原型如下:
char * strtok (char * str, const char * delimiters);

函数说明
strtok()用来将字符串分割成一个个片段。参数str指向欲分割的字符串,参数delimiters则为分割字符串,当strtok()在参数str的字符串中发现到参数delimiters的分割字符时则会将该字符改为'/0'字符。在第一次调用时,strtok()必需给予参数str字符串,往后的调用则将参数str设置成NULL。每次调用成功则返回下一个分割后的字符串指针。

返回值
返回下一个分割后的字符串指针,如果已无从分割则返回NULL。

NetBSD实现:

   1: char*  strtok_r(char* string_org,const char* demial,char** last)
   2: {
   3: const char* spanp; //span表示分隔,p表示指针
   4: char c, sc; //c表示char字符,sc表示 span char
   5: char* tok;  //token表示分隔的段
   6:  
   7: //当开始结尾都为NULL的时候,说明没有字符被查找,所以返回NULL
   8: if (string_org == NULL  && (string_org = *last) == NULL)
   9:     {
  10:     return (NULL);
  11:     }
  12:  
  13: //由goto组成的循环是在扫描字符串的时候,当遇到所需要匹配的字符时,略过这个字符。        
  14: cout:
  15: c = *string_org++;
  16:     
  17: for (spanp = demial; (sc = *spanp++) != 0; )
  18:     {
  19:     if (c == sc)
  20:         {
  21:         goto count;
  22:         }
  23:     }
  24:  
  25: //下一个字符为0,则表示到达了搜索结果,把last置为NULL,并返回NULL            
  26: if (c == 0)
  27:     {
  28:     *last = NULL;
  29:     return (NULL);
  30:     }
  31:  
  32: //把原始的字符串指针回退。            
  33: tok = string_org -1;
  34:  
  35: //开始扫描字符串中是否含有要匹配的字符,之后把这个匹配字符之前的部分返回。
  36: //这看似是个无限循环,但当源字符串和匹配字符串都走到结尾时,也就是string_org和sc都为NULL时,最外层循环最后会走到return(tok)结束循环。
  37: for (;;)
  38:     {
  39:     c = *string_org++;
  40:     spanp = demial;
  41:     
  42:     do 
  43:         {
  44:         if ((sc = *spanp++) == c) 
  45:             {
  46:             if (c == 0)
  47:                 {
  48:                 string_org = NULL;
  49:                 }
  50:             else
  51:                 {
  52:                 string_org[-1] = 0;
  53:                 }
  54:             *last = string_org;
  55:             return (tok);
  56:             }
  57:         } while (sc != 0);
  58:     }
  59:     
  60: }

在NetBSD中strtok的实现:

   1: //把last设置为一个静态局部变量来保存余下内容的地址。
   2: char *
   3: strtok(char *s, const char *delim)
   4:     {
   5:     static char *lasts;
   6:  
   7:     return strtok_r(s, delim, &lasts);
   8:     }

微软实现:

   1: char*  strtok_r(char* string_org,const char* demial)
   2: {
   3: static unsigned char* last; //保存分隔后剩余的部分
   4: unsigned char* str;         //返回的字符串
   5: const unsigned char* ctrl = (const unsigned char*)demial;//分隔字符
   6:  
   7: //把分隔字符放到一个索引表中。定义32是因为ASCII字符表最多是0~255个,也是说用最大的255右移3位,也就是除以8一定会是32中的一个数。
   8: unsigned char map[32]; 
   9: int count;
  10:  
  11: //把map全部清为0,之后相与的操作,与0的都为0
  12: for (count =0; count <32; count++)
  13:     {
  14:     map[count] = 0;
  15:     }
  16:  
  17: //把匹配字符放入表中
  18: //放入的算法是把匹配字符右移3位,相当于除以8,的数值 并上(加上)
  19: //匹配字符与7,得到低3位,得出的结果,是把1左移的位数。最大左移位数是7,也就是所表示的最大值是128,    
  20: do 
  21:     {
  22:     map[*ctrl >> 3] |= (1 << (*ctrl & 7));
  23:     } while (*ctrl++);
  24:     
  25: //原始字符串是否为空,如果为空表示第二次获取剩余字符的分隔部分。    
  26: if (string_org)
  27:     {
  28:     str = (unsigned char*)string_org;
  29:     } 
  30: else
  31:     {
  32:     str = last;
  33:     }
  34:  
  35: //在表中查找是否有匹配的字符,如果有略过    
  36: while ((map[*str >> 3] & (1 << (*str & 7)))  && *str)
  37:     {
  38:     str++;
  39:     }
  40:  
  41: //重置需要扫描的字符串    
  42: string_org = (char*)str;
  43:  
  44: //开始扫描
  45: for (;*str; str++)
  46:     {
  47:     if ( map[*str >> 3] & (1 << (*str & 7)))
  48:         {
  49:         *str++ = '/0';//当找到时,把匹配字符填为0,并且把str指向下一位。
  50:         break; //退出循环             
  51:         }
  52:             
  53:     }
  54:     
  55: last =str; // 把剩余字符串的指针保存到静态变量last中。
  56:     
  57: if (string_org == (char*)str)
  58:     {
  59:     return NULL; //没有找到,也就是没有移动指针的位置,返回NULL
  60:     }
  61: else
  62:     {
  63:     return string_org; //找到了,返回之前字符串的头指针
  64:     }
  65: }

 

对比:NetBSD的方法是节约了空间,牺牲了时间(它的时间复杂度为N2)
     而微软的方法是节约了时间(它的时间复杂度为N),牺牲了空间(开了一个32个8位的空间)

;