C函数之strtok使用及Win & BSD实现
这几天在学习TCP/IP Sockets in C(2nd) Practical Guide for Programmers,在学到如何确认Client和Server之间的数据传输时是一段数据的begin和end时,作者讲了两种方法:
第一种:每次传固定长度的数据,当然多长你要在Client和Server之间协调好。
第二种:规定begin和end的token-标记,(当然你传的数据要是出现你规定的token,也是有办法解决的,不是本文要说的)
好了,问题出来了,如何识别token来把token头和token尾之间真正的数据拿出来呢?
作者用到了本文的主角——strtok函数,翻《C程序设计语言》(我也想man的,可是deepin没装,奇怪~~~)看看如何说:

不晓得大家看不看得懂,反正我没咋看懂,不过应该能把token之间的数据分出来~~~
回来上网再查查,看看一半咋用它,要是能实现就最好了~~~
整理如下:
strtok Sample Code2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <string.h>
char *strtok_bsd(char *s, const char *delim);
char *strtok_win(char *s, const char *delim);
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;
} strtok_bsd Code
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
char *strtok_r_bsd(char *s, const char *delim)
{
const char *spanp; //span表示分隔,p表示指针
char c, sc; //c表示char字符,sc表示 span char
char *tok; //token表示分隔的段
static char *last; //把last设置为一个静态局部变量来保存余下内容的地址。
//当开始结尾都为NULL的时候,说明没有字符被查找,所以返回NULL
if (s == NULL && (s = *last) == NULL)
return (NULL);
//由goto组成的循环是在扫描字符串的时候,当遇到所需要匹配的字符时,略过这个字符。
cont:
c = *s++;
for (spanp = delim; (sc = *spanp++) != 0; )
{
if (c == sc)
goto cont;
}
//下一个字符为0,则表示到达了搜索结果,把last置为NULL,并返回NULL
if (c == 0)
{
*last = NULL;
return (NULL);
}
//把原始的字符串指针回退。
tok = s - 1;
//开始扫描字符串中是否含有要匹配的字符,之后把这个匹配字符之前的部分返回。
//这看似是个无限循环,但当源字符串和匹配字符串都走到结尾时,也就是s和sc都为NULL时,最外层循环最后会走到return(tok)结束循环。
for (;;)
{
c = *s++;
spanp = delim;
do
{
if ((sc = *spanp++) == c)
{
if (c == 0)
s = NULL;
else
s[-1] = 0;
*last = s;
return (tok);
}
}
while (sc != 0);
}
} strtok_win Code
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
char *strtok_win(char *s, const char *delim)
{
static unsigned char *last; //保存分隔后剩余的部分
unsigned char *str; //返回的字符串
const unsigned char *ctrl = (const unsigned char *)delim; //分隔字符
//把分隔字符放到一个索引表中。定义32是因为ASCII字符表最多是0~255个,也是说用最大的255右移3位,也就是除以8一定会是32中的一个数。
unsigned char map[32];
int count;
//把map全部清为0,之后相与的操作,与0的都为0
for (count = 0; count < 32; count++)
map[count] = 0;
//把匹配字符放入表中
//放入的算法是把匹配字符右移3位,相当于除以8,的数值 并上(加上)
//匹配字符与7,得到低3位,得出的结果,是把1左移的位数。最大左移位数是7,也就是所表示的最大值是128,
do
{
map[*ctrl >> 3] |= (1 << (*ctrl & 7));
}
while (*ctrl++);
//原始字符串是否为空,如果为空表示第二次获取剩余字符的分隔部分。
if (s)
str = (unsigned char *)s;
else
str = last;
//在表中查找是否有匹配的字符,如果有略过
while ((map[*str >> 3] & (1 << (*str & 7))) && *str)
str++;
//重置需要扫描的字符串
s = (char *)str;
//开始扫描
for ( ; *str; str++)
{
if ( map[*str >> 3] & (1 << (*str & 7)))
{
*str++ = '\0';//当找到时,把匹配字符填为0,并且把str指向下一位。
break; //退出循环
}
}
last = str; // 把剩余字符串的指针保存到静态变量last中。
if (s == (char *)str)
return NULL; //没有找到,也就是没有移动指针的位置,返回NULL
else
return s; //找到了,返回之前字符串的头指针
}
ps:将strtok换成strtol_bsd和strtok_win就ok了,再单步调试看看~~~
NetBSD的方法是节约了空间,牺牲了时间(它的时间复杂度为N2),而微软的方法是节约了时间(它的时间复杂度为N),牺牲了空间(开了一个32个8位的空间。

- 1楼fovwin昨天 20:27
- 参考了:http://www.cppblog.com/yinquan/archive/2009/06/01/86411.htmlnhttp://bbs.csdn.net/topics/300003007