到处是“坑”的strtok()—解读strtok()的隐含特性
来源:互联网 发布:wind金融数据客户端 编辑:程序博客网 时间:2024/05/16 14:55
在用C/C++实现字符串处理逻辑时,strtok函数的使用非常广泛,其主要作用是按照给定的字符集分隔字符串,并返回各子字符串。由于该函数的使用有诸多限制,如果使用不当就会造成很多“坑”,因此本文首先介绍那些经常误踩的坑,然后通过分析源代码,解读该函数的诸多隐含特性,以便对该函数有个全面的理解,不再被坑。
那些年一起踩过的坑
TOP1 不可重入
目前大部分程序都是在多线程环境下运行的,因此函数的可重入性就显得尤为重要。下面实例的本意是,先按照;分隔句子并输出,然后再按照空格分隔单词并输出,可结果呢?
#include <stdio.h>#include <string.h>int main(void){ char szTest[] = "Hello world;I'm Pele"; /* 以;作为句子的分隔符,以空格作为单词的分隔符 */ char *pSentence = NULL; char *pWord = NULL; /* 先分隔句子 */ pSentence = strtok(szTest, ";"); while (NULL != pSentence) { printf("The sentence is %s.\n", pSentence); /* 再分隔单词 */ pWord = strtok(pSentence, " "); while (NULL != pWord) { printf("The word is %s.\n", pWord); pWord = strtok(NULL, " "); } pSentence = strtok(NULL, ";"); } return 0;}
预期结果如下:
$ gcc test_strtok.c$ ./a.outThe sentence is Hello world.The word is Hello.The word is world.The sentence is I'm Pele.The word is I'm.The word is Pele.
可实际结果如下:
$ gcc test_strtok.c$ ./a.outThe sentence is Hello world.The word is Hello.The word is world.
实际告诉我们:strtok不可重入。
TOP2 源字符串会被修改
如果一个字符串在我们的视线之外被修改了,那么可能会发生一些列诡异的事,而我们却全然不知。请看如下案例:
#include <stdio.h>#include <string.h>int main(void){ char szTest[] = "Hello world;I'm Pele"; char *pSentence = NULL; printf("The original string is %s.\n", szTest); pSentence = strtok(szTest, ";"); while (NULL != pSentence) { pSentence = strtok(NULL, ";"); } printf("The final string is %s.\n", szTest); return 0;}
预期结果如下:
$ gcc test_strtok.c$ ./a.outThe original string is Hello world;I'm Pele.The final string is Hello world;I'm Pele.
可实际结果如下:
$ gcc test_strtok.c$ ./a.outThe original string is Hello world;I'm Pele.The final string is Hello world.
实际告诉我们:你传入的字符串会被strtok修改。
TOP3 连续的分隔符被当做一个分隔符处理
如果两个分隔符连续出现,那么在分隔的时候,你是希望分隔出一个空字符串,还是希望strtok忽略掉多余的分隔符呢?请看strtok给我们的答案:
#include <stdio.h>#include <string.h>int main(void){ char szTest[] = "Hello world;;I'm Pele"; /* 连续使用两个;分隔语句 */ char *pSentence = NULL; pSentence = strtok(szTest, ";"); while (NULL != pSentence) { printf("The sentence is %s.\n", pSentence); pSentence = strtok(NULL, ";"); } return 0;}
实际结果如下:
$ gcc test_strtok.c$ ./a.outThe sentence is Hello world.The sentence is I'm Pele.
实际告诉我们:连续出现的分隔符只被处理一次。
TOP4 字符串首尾的分隔符会被忽略
你希望将字符串首尾的分隔符忽略掉吗?如果不希望,那么请慎用strtok,请看:
#include <stdio.h>#include <string.h>int main(void){ char szTest[] = ";Hello world;I'm Pele;;"; char *pSentence = NULL; pSentence = strtok(szTest, ";"); while (NULL != pSentence) { printf("The sentence is %s.\n", pSentence); pSentence = strtok(NULL, ";"); } return 0;}
实际结果如下:
$ gcc test_strtok.c$ ./a.outThe sentence is Hello world.The sentence is I'm Pele.
实际告诉我们:字符串首尾的分隔符会被忽略。
真相只有一个
下面的代码取自glibc-2.20的strtok.c
文件,未做任何删改,中文注释为笔者添加,用于说明造成以上坑的原因。
#include <string.h>static char *olds; /* 使用了全局变量,因此该函数不可重入--TOP1坑 */#undef strtok#ifndef STRTOK# define STRTOK strtok#endif/* Parse S into tokens separated by characters in DELIM. If S is NULL, the last string strtok() was called with is used. For example: char s[] = "-abc-=-def"; x = strtok(s, "-"); // x = "abc" x = strtok(NULL, "-="); // x = "def" x = strtok(NULL, "="); // x = NULL // s = "abc\0=-def\0"*/char *STRTOK (char *s, const char *delim){ char *token; if (s == NULL) s = olds; /* 跳过了字符串前面的分隔符,如果字符串只剩下尾部的分隔符,跳过前导符相当于忽略尾部的分隔符--TOP4坑 */ /* Scan leading delimiters. */ s += strspn (s, delim); if (*s == '\0') { olds = s; return NULL; } /* Find the end of the token. */ token = s; s = strpbrk (token, delim); if (s == NULL) /* This token finishes the string. */ olds = __rawmemchr (token, '\0'); else /* 找到一个分隔符就返回,下次进入该函数会跳过前导分隔符,此为TOP3坑 */ { /* Terminate the token and make OLDS point past it. */ *s = '\0'; /* 将分隔符所在位置置0,此为TOP2坑 */ olds = s + 1; } return token;}
总结
- 尽量使用可重入版的strtok,Windows平台下为strtok_s,Linux平台下为strtok_r。
- 牢记strtok函数族的分隔规则:忽略字符串前后的分隔符,连续的分隔符被当做一个处理。
- 在使用strtok前,请对源字符串进行备份,除非你可以接受字符串被修改这一事实。
0 0
- 到处是“坑”的strtok()—解读strtok()的隐含特性
- 库函数strtok的源代码解读
- strtok是个“失败”的设计
- 又是strtok导致的bug
- strtok函数的应用
- strtok函数的用法
- strtok函数的实现
- 自己写的strtok
- strtok函数的用法
- strtok函数的实现
- MSDN上的strtok
- strtok的用法
- strtok的使用
- strtok()函数的实现
- strtok的用法
- 库函数strtok的实现
- strtok函数的使用方法
- strtok()函数的使用
- [Django]Django Python3出现Error loading MySQLdb module:No module named ‘MySQLdb’问题
- MAC 系统下的腾讯企业邮箱设置
- 设计模式15年后再回首
- 在MapControl中图层不显示的问题
- CAS server配置教程
- 到处是“坑”的strtok()—解读strtok()的隐含特性
- PHP 开发遇到数据库 The user sepcified as a definer ('root'@'%') does not exist 错误 解决方法
- jQuery 使用案例
- 写在成为优秀开发人员之前
- HTML <!DOCTYPE> 标签
- PHP 开发中遇到的 Commands out of sync; you cant't run this command now 错误 解决方法
- mysql的权限分配和回收
- AppCan获得B轮1亿元融资并宣布移动引擎开源
- 1067. Sort with Swap(0,*) (25)