当我们需要将一个数组根据某个字符或字串进行分割成数组的时候,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()函数相关推荐

  1. android 点击事件消费,Android View事件分发和消费源码简单理解

    Android View事件分发和消费源码简单理解 前言: 开发过程中觉得View事件这块是特别烧脑的,看了好久,才自认为看明白.中间上网查了下singwhatiwanna粉丝的读书笔记,有种茅塞顿开 ...

  2. 结合源码深入理解Android Crash处理流程

    应用程序crash在开发过程中还是很常见的,本文主要是从源码的角度去跟踪下Android对于crash的处理流程.App crash的全称:Application crash.而Crash又分为:na ...

  3. Android学习之Activity源码的理解(一)

    一.Activity为Android系统中四大组件之一,是Android程序的呈现层,并通过界面与用户进行交互,因此理解Activity源码是有必要的. 二.之前我写过一篇文章:http://blog ...

  4. PX4姿态解算源码原理理解

    PX4源码原理理解一.主要参考资料链接:1.1 取PX4源码一小部分姿态解算来进行讲解姿态解算源码中文注释:https://blog.csdn.net/zouxu634866/article/deta ...

  5. 发卡网源码 去后门 可以扩展码支付

    介绍: 网源码 去后门 可以扩展码 网盘下载地址: http://kekewangLuo.net/MWiTNkLA7sh0 图片:

  6. LruCache源码的理解

    LruCache源码的理解 使用场景 在Android手机上加载图片,一般会用到三级缓存策略 内存的缓存策略,一般会用到LruCache来解决 内存用于缓存遇到的问题 1. 手机给每个应用分配的内存空 ...

  7. 最新COS美图在线写真站源码+去授权版

    正文: 价值200_元的美图在线写真站源码去授权版,无需授权,源码上传服务器即可使用,网站内容自行在config.php配置. 程序: wwefss.lanzoul.com/iOhZC0cok65i ...

  8. PHP表白网页制作LM系统源码+去授权美化版

    正文: LM在线表白网页制作PHP源码 去授权美化版,更新说明: 1:美化. 2:生成模板专门搞了一个文件夹装了起来,3:本地化,生成的文件在/data里面, 5:去授权,这个是无授权的整套系统 安装 ...

  9. 2022最新物联网卡管理平台源码+去授权的

    正文: 2022最新物联网卡管理平台源码+去授权的,最新版本的,已经是去除授权的,有兴趣的自己上传访问安装就行,多的就没什么可介绍的了. 程序: wwurh.lanzoup.com/i7M4Z07ia ...

最新文章

  1. Udacity机器人软件工程师课程笔记(三十三) - 蒙特卡洛定位算法(MCL)
  2. 文本分类的基本思想和朴素贝叶斯算法原理
  3. 研究javascript中的this
  4. 补一点COM的知识,有关_com_ptr_t
  5. 如何理解遗传算法中的编码与解码?以二进制编码为例
  6. PaperNotes(12)-Autoregressive Quantile networks for generative modeling
  7. Win10自带的邮件email上如何登录qq邮箱
  8. [exgcd] Jzoj P5855 吃蛋糕
  9. 循序渐进Linux目录
  10. 构建Raspberry Pi,脉冲传感器和LCD的心跳计数器
  11. python开发的项目管理软件_10 个超好用的免费开源项目管理软件
  12. div+css静态网页设计——代码质量好-海贼王(6页) 学生漫画网页设计模板代码 漫画网页制作模板 学生简单动漫网站设计成品
  13. 在飞书搞了个机器人,我让ChatGPT帮忙写算法
  14. 为InfiniBand而哭泣
  15. Redis-NOSQL基础
  16. u-collapse折叠面板双层for循环渲染无法加载内层
  17. 不能直接打开excel文件的原因
  18. Matlab中的DSP应用中的impinvar函数
  19. React 中的虚拟 DOM 是什么?
  20. 【小米oj】 小米兔跳格子

热门文章

  1. python2实现模拟登陆UESTC信息门户
  2. 应对衰退就是裁员?硅谷学学微软刷新吧
  3. 在做vue h5 时因改变手机字体导致页面布局错乱 比如在微信公众号中 放入h5 客户把手机字体或者微信字体调大 导致页面错乱
  4. (一)语言基础——JAVA语言
  5. Linux命令之重启系统reboot
  6. 做了10年烟草制造业转行进入IT的开始
  7. 船闸计算机管理工资怎么样,船闸处事业编工资
  8. 电脑我的世界服务器无限刷甘蔗,我的世界怎么无限刷甘蔗_我的世界无限刷甘蔗视频教程_玩游戏网...
  9. linux fsck原理,文件系统及fsck设计思路理解
  10. [圆方树][树链剖分][set]JZOJ 5909 跑商