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 ...
随机推荐
- nodpad++正则替换
则表达式是一个查询的字符串,它包含一般的字符和一些特殊的字符,特殊字符可以扩展查找字符串的能力,正则表达式在查找和替换字符串的作用不可忽视,它 能很好提高工作效率. EditPlus的查找,替换,文件 ...
- 调bug的一点感悟
出错时一定要先看错误日志,要知道出什么错了,所以平常在可能出错的地方都要输出错误日志. 不要根据脑中的设想去调bug,时间久了就没有耐心,一烦躁起来,思维定势了,就越调不出来了. 所以一般半小时还找不 ...
- MVC 的知识
MVC 的知识 下载地址: 1. NET Framework4下载地址: http://www.microsoft.com/downloads/zh-cn/details.aspx?FamilyI ...
- 无开发经验,初学python
1.无开发经验,初学python 如果你不会其他语言,python是你的第一门语言: A Byte of Python (简明python教程,这个有中文版简明 Python 教程)是非常好的入门 ...
- hihoCoder 1039字符消除 (字符串处理)
http://hihocoder.com/problemset/problem/1039 因为字符串只由3种字母组成,并且插入的字符也只能是这三种字符的其中一个,那么可以考虑枚举这三个字符其中一个字符 ...
- Mtk Android 打包解包*.img
打包/解包 boot.img, system.img, userdata.img, or recovery.img [DESCRIPTION] MTK codebase编译出来的image必须使用MT ...
- mongoDB使用复制还原数据库节省空间
用db.copyDatabase可以备份复制数据的方法. 1.db.copyDatabase("from","to","127.0.0.1:16161 ...
- C++ STL 中erase()的使用需要小心
C++ STL极大的方便了用户编写程序,但是同时一不小心也会犯一些错误,如erase()造成迭代器失效经常会引起错误. 错误示例: std::list< int> List; std::l ...
- ural1221. Malevich Strikes Back!
http://acm.timus.ru/problem.aspx?space=1&num=1221 算是枚举的 题目意思是必须划出这样的 11011 10001 00000 10001 110 ...
- CSS控制超链接
一.伪类 CSS控制元素的某种状态---偽类(用于向某些选择器添加特殊的效果) 偽类的语法:元素标签 偽类名称{属性:属性值;} 二.超链接 a:link:未访问的链接 ...