PHP源码之explode使用说明

当我们需要将一个数组根据某个字符或字串进行分割成数组的时候,explode用的很happy,但是你知道~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)

(0)

相关推荐

  • php中explode与split的区别介绍

    首先来看下两个方法的定义: 函数原型:array split (string $pattern, string $string [, int $limit]) 函数原型:array explode ( string $separator, string $string [, int $limit]) 初看没有啥差别,貌似功能都一样.我就犯了这个错误. 请注意两个函数的第一个参数string $pattern和string separator,一个是$pattern说明是正则字符串,一个是$sep

  • php中利用explode函数分割字符串到数组

    分割字符串 //利用 explode 函数分割字符串到数组 复制代码 代码如下: <?php $source = "hello1,hello2,hello3,hello4,hello5";//按逗号分离字符串 $hello = explode(',',$source); for($index=0;$index<count($hello);$index++) { echo $hello[$index];echo "</br>"; } ?>

  • php字符串分割函数explode的实例代码

    array explode (string $separator, string $string [, int $limit]) 该函数有3个参数,第一个参数$separator设置一个分割字符(串).第二个参数$string指定所要操作的字符串.$limit参数是可选的,指定最多将字符串分割为多少个子串.该函数返回一个由被分割的子串组成的数组. 来看下面的例子,对一个由逗号分隔的多行文本数据进行分析.例1,分割字符串. 复制代码 代码如下: <?php$this_year = 2013;$te

  • PHP explode()函数用法、切分字符串

    复制代码 代码如下: <? // ### 切分字符串 #### function jb51netcut($start,$end,$file){ $content=explode($start,$file); $content=explode($end,$content[1]); return $content[0]; } ?> explode定义和用法 explode() 函数把字符串分割为数组. 语法 explode(separator,string,limit) 参数 描述 separat

  • PHP的explode和implode的使用说明

    说到php,函数便长短常主要的,也是php爱好者,和喜好php进阶的朋友们必须控制的东东,学习php的友朋们也晓得,数组也是必需把握的,能够那样道,进阶php,也便是学习php中的数组而在数组中通常要用到分割字符串啊什么的,便要用到explode ,和implode函数,下里说道php中的explode 和implode的用法,芭蕾舞鞋,盼望对于友朋们有所辅助! explode在PHP中的用法 (PHP 3, PHP 4, PHP 5) explode--使用一个字符串分割另一个字符串描写 ar

  • php使用explode()函数将字符串拆分成数组的方法

    本文实例讲述了php使用explode()函数将字符串拆分成数组的方法.分享给大家供大家参考.具体分析如下: explode()函数:字符串拆分成数组 示例代码如下: <?php $str = "朝阳区,海淀区,西城区,东城区,丰台区"; $arr = explode(",",$str); echo "<pre>"; print_r($arr); ?> 结果如下: <pre>Array ( [0] =>

  • PHP数组和explode函数示例总结

    PHP数组和explode函数应用实例,供大家学习参考. 例1: <?php $province = array("北京","上海","天津","重庆","河北","山西","内蒙古","辽宁","吉林","黑龙江","江苏","浙江","安徽"

  • PHP源码之explode使用说明

    当我们需要将一个数组根据某个字符或字串进行分割成数组的时候,explode用的很happy,但是你知道-explode是怎么工作的么-- 首先可以肯定的是,explode也是会分配空间的,毫无疑问. 复制代码 代码如下: //文件1:ext/standard/string.c //先来看下explode的源代码 PHP_FUNCTION(explode) { char *str, *delim; int str_len = 0, delim_len = 0; long limit = LONG_

  • 分享PHP源码批量抓取远程网页图片并保存到本地的实现方法

    做为一个仿站工作者,当遇到网站有版权时甚至加密的时候,WEBZIP也熄火,怎么扣取网页上的图片和背景图片呢.有时候,可能会想到用火狐,这款浏览器好像一个强大的BUG,文章有版权,屏蔽右键,火狐丝毫也不会被影响. 但是作为一个热爱php的开发者来说,更多的是喜欢自己动手.所以,我就写出了下面的一个源码,php远程抓取图片小程序.可以读取css文件并抓取css代码中的背景图片,下面这段代码也是针对抓取css中图片而编写的. <?php header("Content-Type: text/ht

  • PHP实现长文章分页实例代码(附源码)

    当文章内容比较长,为了更好的满足用户体验度,我们将文章内容分页显示处理,而一般分页处理是在后台发布文章的时候就将提交的内容生成多个分页后的静态文件.通过本文结合实例采用php动态将长文章内容进行分页处理. 查看效果演示     源码下载 如何分页 手动分页:一般在编辑内容时加入特殊分页标记,如{pages},提交后,PHP程序会根据分页符处理分页,生成不同的静态页面.这种分页方法分页准确,但是需要人工手动添加分页符,工作量大. 自动分页:PHP程序会根据设置好的分页符将内容进行分页,然后生成不同

  • jqPlot 图表中文API使用文档及源码和在线示例

    简介 jqplot是一款非常不错的基于jquery的图表插件,这篇文章主要帮助大家整理了jqplot的中文使用说明和在线示例及源码下载.jqplot插件在支持HTML5的浏览器上将会在canvas上呈现图表. 引入脚本文件 jqplot需要1.4.3以上的jquery版本支持,刚才说过了,jqplot在支持HTML5 Canvas的浏览器上将以canvas呈现,否则,如果你在IE9以下版本的浏览器上则要引入excanvas.js文件,当然,jqplot的CSS文件也要同时引入,代码如下: 复制代

  • Java实现简易版联网坦克对战小游戏(附源码)

    介绍 通过本项目能够更直观地理解应用层和运输层网络协议, 以及继承封装多态的运用. 网络部分是本文叙述的重点, 你将看到如何使用Java建立TCP和UDP连接并交换报文, 你还将看到如何自己定义一个简单的应用层协议来让自己应用进行网络通信. 获取源码 (本地下载) 基础版本 游戏的原理, 图形界面(非重点) 多张图片快速连续地播放, 图片中的东西就能动起来形成视频, 对视频中动起来的东西进行操作就变成游戏了. 在一个坦克对战游戏中, 改变一辆坦克每一帧的位置, 当多帧连续播放的时候, 视觉上就有

  • SpringBoot从0到1整合银联无跳转支付功能附源码

    前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容. 提示:以下是本篇文章正文内容,下面案例可供参考 一.官网 https://open.unionpay.com/tjweb/user/mchTest/param 个人登录后的相关参数查看 其实在接入支付之前建议大家了解下 对称加密,分对称加密RSA之类,摘要算法,https,证书等这些知识点,因为此代码后面的验签,判断都是基于此的

  • Android串口通信apk源码详解(附完整源码)

    1.SerialPortHelper「Android串口通信」介绍 原项目地址 https://github.com/freyskill/SerialPortHelper Android串口通讯助手可以用于需要使用串口通信的Android外设,该库有如下特点: 1.串口通信部分使用C++实现,在笔者接触的部分设备上实测,使用C++实现与Google官方提供的Demo的方式要快: 2.支持且必须设置串口接收最大数据长度,初始化库时填入该参数,这样设置的原因是考虑在实际使用中,规定的串口通信协议格式

  • python编写adb截图工具的实现源码

    目录 一. 功能 二.使用说明 三.实现 1.初始源码 2.优化:增加ip连接断开重连处理 一. 功能 Android端或者Android终端的远程截图至本地电脑中 二.使用说明 1.adb截图工具可用于Android手机及Android终端 2.使用数据线连接前提:电脑与Android终端/手机已连接 Android终端/手机已打开开发者模式 3.生成的图片格式为png 三.实现 1.初始源码 import time import os,sys #截图 def screencap_cmd(fi

  • 14 个Python小游戏 源码分享

    目录 1.吃金币 2.打乒乓 3.滑雪 4.并夕夕版飞机大战 5.打地鼠 6.小恐龙 7.消消乐 8.俄罗斯方块 9.贪吃蛇 10.24点小游戏 11.平衡木 12.外星人入侵 13.贪心鸟 14.井字棋888'' 1.吃金币 源码分享: import os import cfg import sys import pygame import random from modules import * '''游戏初始化''' def initGame(): # 初始化pygame, 设置展示窗口

  • 使用Python实现管理系统附源码

    目录 0. 前言 1. 题目要求 2. 要求分析 3.效果展示 3.1 主界面 3.2 注册界面 3.3 管理员 3.3.1 管理员登陆界面 3.3.2 管理员——疫苗信息管理界面 3.3.3 管理员——社区医院管理界面 3.4 社区医院 3.4.1 社区医院登陆界面 3.4.2 社区医院——医院信息管理界面 3.4.3 社区医院——用户预约界面 3.4.5 社区医院——接种信息管理界面 3.4.6 社区医院——不良反应反馈界面 3.5 普通用户 3.5.1 普通用户登陆界面 3.5.2 普通用

随机推荐