php explode 效率,从源码去理解PHP的explode()函数
当我们需要将一个数组根据某个字符或字串进行分割成数组的时候,explode()函数很好用,但是你知道explode()是怎么工作的么?截取字串的问题,都会避免不了重新分配空间的消耗,explode也是会分配空间的,毫无疑问。
//文件1:ext/standard/string.c
//先来看下explode的源代码
PHP_FUNCTION(explode)
{
char *str, *delim;
int str_len = 0, delim_len = 0;
long limit = LONG_MAX; /* No limit */
zval zdelim, zstr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &delim, &delim_len, &str, &str_len, &limit) == FAILURE) {
return;
}
if (delim_len == 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
RETURN_FALSE;
}
//这里会开辟一个数组,用来存放分割后的数据
array_init(return_value);
//因为这个,我们用explode('|', '');成为了合法的
if (str_len == 0) {
if (limit >= 0) {
add_next_index_stringl(return_value, "", sizeof("") - 1, 1);
}
return;
}
//下面这两个是将原字串和分割符都构建成_zval_struct 结构,
//ZVAL_STRINGL会分配空间哦~~源代码随后贴出
ZVAL_STRINGL(&zstr, str, str_len, 0);
ZVAL_STRINGL(&zdelim, delim, delim_len, 0);
//limit值是explode中允许传递的explode的第三个参数,它允许正负
if (limit > 1) {
php_explode(&zdelim, &zstr, return_value, limit);
} else if (limit < 0) {
php_explode_negative_limit(&zdelim, &zstr, return_value, limit);
} else {
add_index_stringl(return_value, 0, str, str_len, 1);
}
}
再来看一段:
//ZVAL_STRINGL的源代码:
//文件2:zend/zend_API.c
#define ZVAL_STRINGL(z, s, l, duplicate) { \
const char *__s=(s); int __l=l; \
Z_STRLEN_P(z) = __l; \
Z_STRVAL_P(z) = (duplicate?estrndup(__s, __l):(char*)__s);\
Z_TYPE_P(z) = IS_STRING; \
}
....
//estrndup才是主菜:
//文件3:zend/zend_alloc.h
#define estrndup(s, length) _estrndup((s), (length) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
....
//_estrndup的实现: zend/zend_alloc.c
ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
char *p;
p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
if (UNEXPECTED(p == NULL)) {
return p;
}
memcpy(p, s, length); //分配空间
p[length] = 0;
return p;
}
//另外在substr和strrchr strstr中用到的ZVAL_STRING也是使用了上诉的实现
下面根据explode的第三个参数limit来分析调用:条件对应的是explode中最后的三行,对limit条件的不同。注: limit在缺省的时候(没有传递),他的默认值是LONG_MAX,也就是属于分支1的情况。
1、limit > 1 :
调用php_explode方法,该方法也可以在ext/standard/string.c中找到,并且是紧接着explode实现的上面出现(所以在查找本函数中调用来自本文件的方法的时候很方便,几乎无一列外都是在该函数的紧接着的上面^_^)。
PHPAPI void php_explode(zval *delim, zval *str, zval *return_value, long limit)
{
char *p1, *p2, *endp;
//先得到的是源字串的末尾位置的指针
endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);
//记录开始位置
p1 = Z_STRVAL_P(str);
//下面这个是获得分割符在str中的位置,可以看到在strrpos和strpos中也用到了这个方法去定位
p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);
if (p2 == NULL) {
//因为这个,所以当我们调用explode('|', 'abc');是合法的,出来的的就是array(0 => 'abc')
add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1);
} else {
//依次循环获得下一个分隔符的位置,直到结束
do {
//将得到的子字串(上个位置到这个位置中间的一段,第一次的时候上个位置就是开始
add_next_index_stringl(return_value, p1, p2 - p1, 1);
//定位到分隔符位置p2+分隔符的长度的位置
//比如,分隔符='|', 原字串= ’ab|c', p2 = 2, 则p1=2+1=3
p1 = p2 + Z_STRLEN_P(delim);
} while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL &&
--limit > 1);
//将最后的一个分隔符后面的字串放到结果数组中
//explode('|', 'avc|sdf'); => array(0 => 'avc', 1= > 'sdf')
if (p1 <= endp)
add_next_index_stringl(return_value, p1, endp-p1, 1);
}
}
2、limit < 0 :
调用php_explode_negative_limit方法:
PHPAPI void php_explode_negative_limit(zval *delim, zval *str, zval *return_value, long limit)
{
#define EXPLODE_ALLOC_STEP 64
char *p1, *p2, *endp;
endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);
p1 = Z_STRVAL_P(str);
p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);
if (p2 == NULL) {
//它这里竟然没有处理,那explode('|', 'abc', -1) 就成非法的了,获得不了任何值
/*
do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
by doing nothing we return empty array
*/
} else {
int allocated = EXPLODE_ALLOC_STEP, found = 0;
long i, to_return;
char **positions = emalloc(allocated * sizeof(char *));
//注意这里的positions的声明,这个数组是用来保存所有子字串的读取位置
positions[found++] = p1; //当然起始位置还是需要保存
//下面两个循环,第一个是循环所有在字符串中出现的分隔符位置,并保存下一个子字串读取位置起来
do {
if (found >= allocated) {
allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
positions = erealloc(positions, allocated*sizeof(char *));
}
positions[found++] = p1 = p2 + Z_STRLEN_P(delim);
} while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL);
//这个就是从数组中开始获得返回的结果将从哪个子字串开始读
to_return = limit + found;
/* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
for (i = 0;i < to_return;i++) { /* this checks also for to_return > 0 */
add_next_index_stringl(return_value, positions[i],
(positions[i+1] - Z_STRLEN_P(delim)) - positions[i],
1
);
}
efree(positions);//很重要,释放内存
}
#undef EXPLODE_ALLOC_STEP
}
3、limit = 1 or limit = 0 :
当所有第一和第二条件都不满足的时候,就进入的这个分支,这个分支很简单就是将源字串放到输出数组中,explode('|', 'avc|sd', 1) or explode('|', 'avc|sd', 0) 都将返回array(0 => 'avc|sd');
//add_index_stringl源代码
//文件4:zend/zend_API.c
ZEND_API int add_next_index_stringl(zval *arg, const char *str, uint length, int duplicate) /* {{{ */
{
zval *tmp;
MAKE_STD_ZVAL(tmp);
ZVAL_STRINGL(tmp, str, length, duplicate);
return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp, sizeof(zval *), NULL);
}
//zend_hash_next_index_insert
//zend/zend_hash.h
#define zend_hash_next_index_insert(ht, pData, nDataSize, pDest) \
_zend_hash_index_update_or_next_insert(ht, 0, pData, nDataSize, pDest, HASH_NEXT_INSERT ZEND_FILE_LINE_CC)
//zend/zend_hash.c
///太长了~~~~不贴了
可见(不包含分配空间这些),当limit>1的时候,效率是O(N)【N为limit值】,当limit < 0的时候,效率是O(N+M)【N为limit值, M 为分割符出现次数】,当limit=1 or limit=0 的时候, 效率是O(1)。
php explode 效率,从源码去理解PHP的explode()函数相关推荐
- android 点击事件消费,Android View事件分发和消费源码简单理解
Android View事件分发和消费源码简单理解 前言: 开发过程中觉得View事件这块是特别烧脑的,看了好久,才自认为看明白.中间上网查了下singwhatiwanna粉丝的读书笔记,有种茅塞顿开 ...
- 结合源码深入理解Android Crash处理流程
应用程序crash在开发过程中还是很常见的,本文主要是从源码的角度去跟踪下Android对于crash的处理流程.App crash的全称:Application crash.而Crash又分为:na ...
- Android学习之Activity源码的理解(一)
一.Activity为Android系统中四大组件之一,是Android程序的呈现层,并通过界面与用户进行交互,因此理解Activity源码是有必要的. 二.之前我写过一篇文章:http://blog ...
- PX4姿态解算源码原理理解
PX4源码原理理解一.主要参考资料链接:1.1 取PX4源码一小部分姿态解算来进行讲解姿态解算源码中文注释:https://blog.csdn.net/zouxu634866/article/deta ...
- 发卡网源码 去后门 可以扩展码支付
介绍: 网源码 去后门 可以扩展码 网盘下载地址: http://kekewangLuo.net/MWiTNkLA7sh0 图片:
- LruCache源码的理解
LruCache源码的理解 使用场景 在Android手机上加载图片,一般会用到三级缓存策略 内存的缓存策略,一般会用到LruCache来解决 内存用于缓存遇到的问题 1. 手机给每个应用分配的内存空 ...
- 最新COS美图在线写真站源码+去授权版
正文: 价值200_元的美图在线写真站源码去授权版,无需授权,源码上传服务器即可使用,网站内容自行在config.php配置. 程序: wwefss.lanzoul.com/iOhZC0cok65i ...
- PHP表白网页制作LM系统源码+去授权美化版
正文: LM在线表白网页制作PHP源码 去授权美化版,更新说明: 1:美化. 2:生成模板专门搞了一个文件夹装了起来,3:本地化,生成的文件在/data里面, 5:去授权,这个是无授权的整套系统 安装 ...
- 2022最新物联网卡管理平台源码+去授权的
正文: 2022最新物联网卡管理平台源码+去授权的,最新版本的,已经是去除授权的,有兴趣的自己上传访问安装就行,多的就没什么可介绍的了. 程序: wwurh.lanzoup.com/i7M4Z07ia ...
最新文章
- Udacity机器人软件工程师课程笔记(三十三) - 蒙特卡洛定位算法(MCL)
- 文本分类的基本思想和朴素贝叶斯算法原理
- 研究javascript中的this
- 补一点COM的知识,有关_com_ptr_t
- 如何理解遗传算法中的编码与解码?以二进制编码为例
- PaperNotes(12)-Autoregressive Quantile networks for generative modeling
- Win10自带的邮件email上如何登录qq邮箱
- [exgcd] Jzoj P5855 吃蛋糕
- 循序渐进Linux目录
- 构建Raspberry Pi,脉冲传感器和LCD的心跳计数器
- python开发的项目管理软件_10 个超好用的免费开源项目管理软件
- div+css静态网页设计——代码质量好-海贼王(6页) 学生漫画网页设计模板代码 漫画网页制作模板 学生简单动漫网站设计成品
- 在飞书搞了个机器人,我让ChatGPT帮忙写算法
- 为InfiniBand而哭泣
- Redis-NOSQL基础
- u-collapse折叠面板双层for循环渲染无法加载内层
- 不能直接打开excel文件的原因
- Matlab中的DSP应用中的impinvar函数
- React 中的虚拟 DOM 是什么?
- 【小米oj】 小米兔跳格子
热门文章
- python2实现模拟登陆UESTC信息门户
- 应对衰退就是裁员?硅谷学学微软刷新吧
- 在做vue h5 时因改变手机字体导致页面布局错乱 比如在微信公众号中 放入h5 客户把手机字体或者微信字体调大 导致页面错乱
- (一)语言基础——JAVA语言
- Linux命令之重启系统reboot
- 做了10年烟草制造业转行进入IT的开始
- 船闸计算机管理工资怎么样,船闸处事业编工资
- 电脑我的世界服务器无限刷甘蔗,我的世界怎么无限刷甘蔗_我的世界无限刷甘蔗视频教程_玩游戏网...
- linux fsck原理,文件系统及fsck设计思路理解
- [圆方树][树链剖分][set]JZOJ 5909 跑商