strtok是个“失败”的设计

引用请注明出处:http://blog.csdn.net/int64ago/article/details/7416985

话不多说,先来看个例子:

#include  <stdio.h>
#include  <string.h>

void print_tokens(char *line)
{
	static char whitespace[] = " ";
	char *token;

	for(token = strtok(line,whitespace); token != NULL; 
			token = strtok(NULL,whitespace)){
		printf("Next token is %s\n",token);
	}
}

int main(int argc, char *argv[])
{
	char line[] = "we are      friends !! \n";
	//char *line = "we are friends !!";
	int len = strlen(line); 
	print_tokens(line);
	int i;
	for(i = 0; i != len; ++i){
		if(line[i]){
			putchar(line[i]);
		}else {
			putchar('0');
		}
	}
	return 0;
}

这个小程序的运行结果如下:

strtok是个“失败”的设计_第1张图片

      现在分析,strtok这个函数到底干了些什么。相信很多人在处理字符串的时候遇到过分段处理,很简单的例子就是一片英语文章单词的检测,很理所当然的做法也是最方便的做法就是一个一个单词提取处理进行正确性检测。这里就会设计到一个问题:到底什么是一个单词?短文章用人来检测的获取更简单一点,人不但可以判断什么是一个单词,而且词汇量大的人也很容易知道什么是一个单词,比如“what”是一个单词,但是看到“t”就很明显的断定不是单词。但是,计算机没那么聪明,它必须根据人给它的规则来“更快”的办事。这个例子所设计的规则就是怎样提取一个单词(即使是“t”,也算单词),换个思维其实就是单词有什么来区分!比如空格、回车、标点……这个人是很容易描述成计算机所能接受的信息,所以这时strtok就应运而生了。

      它的原型是:char *strtok(char *s, const char *delim); 说明:在s中找出以delim中的字符为分隔的字符串,即是源串中除去了含有分隔串中的所有字符后余下的一段段的字符串,每调用一次找到一串,找不到则返回空串。第一次调用必须传给它有效的字符串,第二次传NULL就可以了,每次调用返回找到的子串的时候都会把源串中该子串的尾部字符(原来是搜索串中的某一字符)修改成NULL字符返回值为每次调用得到的字串。

      其实,简单的说就是delim就是我上面说的规则,s是我们要处理的目标。但是,基于strtok要多次返回不同值其本身的复杂性,理解它变得更遭,因为s并不是我们想的那样。有人觉得为什么可以第一次传目标串,以后就可以传NULL了呢?它是怎么区分我们是第几次调用它的呢?因为传递给它的参数里并没有体现“次数”这个信息。如果看了strtok实现的源码(当然没有都看懂,只看了一个大概)后,会发现函数里面char *___strtok;的常驻局部变量!恰恰是这个量来记录“次数这个信息”。这就引来一个严重问题,不能同时用strtok处理两个以上对象!个人感觉这种做法严重影响了方法之间的独立性,很不符合c及linux的哲学思想,从这方面说它是算一个“失败”的设计!

      再者,如果把上面程序的char line[] = "we are      friends !!" ;改成char *line = "we are friends !!"; 运行时会出现段错误,因为程序访问了不该访问的地址,这两句的区别就是前者是把字符数组初始化,而后者是用字符串常量的地址为指向字符型指针初始化,有点绕,简单说就是char *line = "we are friends !!"是常量,不能改变,而strtok恰恰需要不断改变源字符串,看看运行结果的最后一行就会明白了。这又是一大”败笔“!

     我现在(至少现在)没能力把strtok更好的实现,因为之所以设计strtok的人这样做,我想最大的原因还是考虑到实现的时间效率——线性、空间效率——无额外开销,这点上还是挺不错的,只是苦了应用它的程序员,要小心小心再小心~

你可能感兴趣的:(c,linux,null,token,whitespace)