strtok和strtok_r
1.strtok()函数的用法
函数原型:char *strtok(char *s, const char *delim);
Function:分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。
Description:strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串,当strtok()在参数s的字符串中发现到参数delim的分割字符时 则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回被分割出片段的指针。
(1)如果stork函数的第一个参数不是NULL,函数将找到的字符串的第一个标记。同时保存它在字符串中的位置
(2)如果strtok函数的第一个参数是NULL,函数就在同一个字符串中从这个被保存的位置开始向前面一样查找下一个标记。
下面是一个使用实例:
#include <iostream>
using namespace std; int main(int argc, char * argv[])
{
//时间格式 2010/08/11 10:38:22
char strEventTime[] = "2010/08/11 10:38:22";
char *token = NULL; token = strtok(strEventTime, "/");
char *year = token;
if (token != NULL)
{
token = strtok(NULL, "/");
}
char *month = token;
if (token != NULL)
{
token = strtok(NULL, " ");
}
char *day = token;
if (token != NULL)
{
token = strtok(NULL, ":");
}
char *hour = token;
if (token != NULL)
{
token = strtok(NULL, ":");
}
char *minute = token; if (token != NULL)
{
token = strtok(NULL, ":");
}
char *second = token; printf("%s %s %s %s %s %s %s\n", year, month, day, hour, minute, second);
return ;
}
/*
* strtok是一个线程不安全的函数,因为它使用了静态分配的空间来存储被分割的字符串位置
* 线程安全的函数叫strtok_r
* 运用strtok来判断ip或者mac的时候务必要先用其他的方法判断'.'或':'的个数,
* 因为用strtok截断的话,比如:"192..168.0...8..."这个字符串,strtok只会截取四次,中间的...无论多少都会被当作一个key
*/
2.strtok()函数的实现
(1)NetBSD实现:
: char* strtok_r(char* string_org,const char* demial,char** last)
: {
: const char* spanp; //span表示分隔,p表示指针
: char c, sc; //c表示char字符,sc表示 span char
: char* tok; //token表示分隔的段
:
: //当开始结尾都为NULL的时候,说明没有字符被查找,所以返回NULL
: if (string_org == NULL && (string_org = *last) == NULL)
: {
: return (NULL);
: }
:
: //由goto组成的循环是在扫描字符串的时候,当遇到所需要匹配的字符时,略过这个字符。
: cont:
: c = *string_org++;
:
: for (spanp = demial; (sc = *spanp++) != ; )
: {
: if (c == sc)
: {
: goto cont;
: }
: }
:
: //下一个字符为0,则表示到达了搜索结果,把last置为NULL,并返回NULL
: if (c == )
: {
: *last = NULL;
: return (NULL);
: }
:
: //把原始的字符串指针回退。
: tok = string_org -;
:
: //开始扫描字符串中是否含有要匹配的字符,之后把这个匹配字符之前的部分返回。
: //这看似是个无限循环,但当源字符串和匹配字符串都走到结尾时,也就是string_org和sc都为NULL时,最外层循环最后会走到return(tok)结束循环。
: for (;;)
: {
: c = *string_org++;
: spanp = demial;
:
: do
: {
: if ((sc = *spanp++) == c)
: {
: if (c == )
: {
: string_org = NULL;
: }
: else
: {
: string_org[-] = ;
: }
: *last = string_org;
: return (tok);
: }
: } while (sc != );
: }
:
: }
(2)在NetBSD中strtok的实现:
: //把last设置为一个静态局部变量来保存余下内容的地址。
: char *
: strtok(char *s, const char *delim)
: {
: static char *lasts;
:
: return strtok_r(s, delim, &lasts);
: }
(3)微软的实现:
: char* strtok_r(char* string_org,const char* demial)
: {
: static unsigned char* last; //保存分隔后剩余的部分
: unsigned char* str; //返回的字符串
: const unsigned char* ctrl = (const unsigned char*)demial;//分隔字符
:
: //把分隔字符放到一个索引表中。定义32是因为ASCII字符表最多是0~255个,也是说用最大的255右移3位,也就是除以8一定会是32中的一个数。
: unsigned char map[];
: int count;
:
: //把map全部清为0,之后相与的操作,与0的都为0
: for (count =; count <; count++)
: {
: map[count] = ;
: }
:
: //把匹配字符放入表中
: //放入的算法是把匹配字符右移3位,相当于除以8,的数值 并上(加上)
: //匹配字符与7,得到低3位,得出的结果,是把1左移的位数。最大左移位数是7,也就是所表示的最大值是128,
: do
: {
: map[*ctrl >> ] |= ( << (*ctrl & ));
: } while (*ctrl++);
:
: //原始字符串是否为空,如果为空表示第二次获取剩余字符的分隔部分。
: if (string_org)
: {
: str = (unsigned char*)string_org;
: }
: else
: {
: str = last;
: }
:
: //在表中查找是否有匹配的字符,如果有略过
: while ((map[*str >> ] & ( << (*str & ))) && *str)
: {
: str++;
: }
:
: //重置需要扫描的字符串
: string_org = (char*)str;
:
: //开始扫描
: for (;*str; str++)
: {
: if ( map[*str >> ] & ( << (*str & )))
: {
: *str++ = '\0';//当找到时,把匹配字符填为0,并且把str指向下一位。
: break; //退出循环
: }
:
: }
:
: last =str; // 把剩余字符串的指针保存到静态变量last中。
:
: if (string_org == (char*)str)
: {
: return NULL; //没有找到,也就是没有移动指针的位置,返回NULL
: }
: else
: {
: return string_org; //找到了,返回之前字符串的头指针
: }
: }
(4)对比:
1.NetBSD的方法是节约了空间,牺牲了时间(它的时间复杂度为N2)
2.而微软的方法是节约了时间(它的时间复杂度为N),牺牲了空间(开了一个32个8位的空间)
3.strtok()自己的理解和注释:
自己在NetBSD的基础上加了一些帮助理解的注释:
#ifndef STRTOK_H
#define STRTOK_H #include <stdio.h> // NetBSD:
char *cat_strtok_r(char *src, const char *delim, char **last) {
const char *spanp; // span表示分割,p表示指针
char c, sc; // c表示char字符(保存src中的字符),sc表示span char(保存delim的字符)
char *tok; // 表示分隔的段 // 当开始、结尾都为NULL时,说明没有字符查找
if (NULL == src && NULL == (src = *last))
return NULL; // 由goto组成的循环是在扫描字符串的时候,当遇到所需要匹配的字符时,略过这个字符
cont:
c = *src++; for (spanp = delim; (sc = *spanp++) != ; ) {
if (c == sc)
goto cont; // cat:这里的意思应该是:src字符串头部匹配delim的部分都忽略掉,因为分割了也没意义,匹配部分前面已经没有其他字符了
} // 下一个字符为0,则表示到达了搜索结果,把last置为NULL,并返回NULL
// cat:其实就是到达src的尾部,也就意味着delim和src是相等的,就无从分割了
if ( == c) {
*last = NULL;
return NULL;
} // 把原始的字符串指针回退
tok = src - ; // cat:因为c = *src++;最后一步就是不匹配还是会执行++操作 // 开始扫描字符串中是否含有压迫匹配的字符,之后把该匹配字符之前的部分返回
// 当源字符串和匹配字符串当走到结尾时,即src和sc都为NULL时,最外层循环最后会return(tok)结束循环
for ( ; ; ) {
c = *src++;
spanp = delim;
do {
if ((sc = *spanp++) == c) {
if ( == c)
src = NULL;
else
src[-] = ;
// 比如src为"12:34",匹配字符为":"。当匹配到":"时,src++导致src指向了"3",所以需要减1,把匹配到的位置(即把src中的":")设为'\0'
//*(src - 1) = 0;// 等价于这行代码 *last = src; return tok;
}
} while (sc != );
}
} // 把last设置为一个静态局部变量来保存余下内容的地址
char *cat_strtok(char *src, const char *delim) {
static char *lasts;
return cat_strtok_r(src, delim, &lasts);
} #endif
测试代码:
#include "strtok.h"
void test_strtok();
int main() {
test_strtok();
return ;
}
void test_strtok() {
//char *src = "111:222:333:444";
char *src = "123:456";
char *ret = cat_strtok(src, ":");
printf("%s\n%s\n", ret, src);
}
然后我们会发现程序运行到src[-1] = 0;这一步的时候出错。为什么呢?
先看C语言中内存分布情况(更多请查阅《C语言char[]和char*比较》http://www.cnblogs.com/lingshaohu/p/3956239.html ):
char s[]="abc"; //栈
char *p2; //栈
char *p3=""; //123456\0在常量区,p3在栈上。
也就是说,char *src = "123:456"; "123:456"是一个常量!常量不能修改,而我们src[-1] = 0;尝试修改它,所以报错!
因此,改成这样就可以了:
void test_strtok() {
//char *src = "111:222:333:444";
//char *src = "123:456";
char src[] = "123:456";
char *ret = cat_strtok(src, ":");
printf("%s\n%s\n", ret, src);
}
ref:
http://www.cnblogs.com/aduck/articles/2245364.html
http://www.cppblog.com/yinquan/archive/2009/06/01/86411.html
strtok和strtok_r的更多相关文章
- [转]关于strtok和strtok_r函数的深度研究
在linux环境下,字符串分割的函数中,大家比较常用的是strtok函数,这个函数用处很大,但也有一些问题,以下将深度挖掘一下这个函数的用法,原理,实现,其次,该函数是不可再入函数,但是在linux ...
- 关于函数strtok和strtok_r的使用要点和实现原理(二)
http://www.cnblogs.com/stemon/p/4013264.html已经介绍了使用strtok函数的一些注意事项,本篇将介绍strtok的一个应用并引出strtok_r函数. 1. ...
- 关于函数strtok和strtok_r的使用要点和实现原理(二)【转】
本文转载自:http://astute11.blog.51cto.com/4404646/1334199 (一)中已经介绍了使用strtok函数的一些注意事项,本篇将介绍strtok的一个应用并引出s ...
- 关于函数strtok和strtok_r的使用要点和实现原理
strtok函数的使用是一个老生常谈的问题了.该函数的作用很大,争议也很大.以下的表述可能与一些资料有区别或者说与你原来的认识有差异,因此,我尽量以实验为证.交代一下实验环境是必要的,winxp+vc ...
- 【C】——strtok()和strtok_r()
下面的说明摘自于最新的Linux内核2.6.29,说明了strtok()这个函数已经不再使用,由速度更快的strsep()代替 /** linux/lib/string.c** Copyright ( ...
- 关于函数strtok和strtok_r的使用要点和实现原理(一)【转】
本文转载自:http://astute11.blog.51cto.com/4404646/1334198 strtok函数的使用是一个老生常谈的问题了.该函数的作用很大,争议也很大.以下的表述可能与一 ...
- 字符串分割函数 STRTOK & STRTOK_R (转)
1.一个应用实例 网络上一个比较经典的例子是将字符串切分,存入结构体中.如,现有结构体 typedef struct person{ char name[25]; char sex[1 ...
- STRTOK函数和STRTOK_R函数
STRTOK函数和STRTOK_R函数 注:本文转载自博客园,感谢作者整理! 1.一个应用实例 网络上一个比较经典的例子是将字符串切分,存入结构体中.如,现有结构体 typedef struct pe ...
- [转载]strtok函数和strtok_r函数
1.一个应用实例 网络上一个比较经典的例子是将字符串切分,存入结构体中.如,现有结构体 typedef struct person{ char name[25]; char sex[1 ...
随机推荐
- CMake入门指南-编译教程
CMake是一个比make更高级的编译配置工具,它可以根据不同平台.不同的编译器,生成相应的Makefile或者vcproj项目.通过编写CMakeLists.txt,可以控制生成的Makefile, ...
- Android Touch(3)View的touchDelegate
作用: 基类View有个函数 public void setTouchDelegate(TouchDelegate delegate),给view内部的另一个view设置一个touch代理. 图中vi ...
- php关于static关键字
静态属性与方法可以在不实例化类的情况下调用,直接使用类名::方法名的方式进行调用.静态属性不允许对象使用->操作符调用.静态方法中,$this伪变量不允许使用.可以使用self,parent,s ...
- leetcode:House Robber(动态规划dp1)
You are a professional robber planning to rob houses along a street. Each house has a certain amount ...
- Codeforces Round #209 (Div. 2)C
刷了一页的WA ..终于发现了 哪里错了 快速幂模板里一个变量t居然开得long ... 虽然代码写的丑了点 但是是对的 那个该死的long 啊.. #include <iostream&g ...
- JS 样式操作学习总结。
在我们编写网页的时候,样式表示我们常常需要相伴的内容,谁然很招人烦.恕我前端菜鸟对这东西很是无力啊.下面是我在代码浪潮中的虚席到的一些东西. 1.样式表内容修改方式. 2.当前元素样式内容获取. 3. ...
- HibernateTemplate 查询
Spring中常用的hql查询方法getHibernateTemplate()上 一.find(String queryString); 示例:this.getHibernateTempl ...
- Winform——计算器进制转换
namespace 进制转换2._0 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } p ...
- BZOJ 1123 BLO
tarjan求割点计算答案.注意不是每一棵子树都算答案.开个变量记一下. #include<iostream> #include<cstdio> #include<cst ...
- IOS中UIWebView执行javaScript脚本时注意点
1.webView之所以能够滚动,因为它内部有一个UIScrollView子控件 2.移除webView顶部和底部灰色的一层view * 遍历webView中scrollView内部的所有子控件 * ...