当前位置:Gxlcms > PHP教程 > PHP源码之explode使用说明_php技巧

PHP源码之explode使用说明_php技巧

时间:2021-07-01 10:21:17 帮助过:33人阅读

当我们需要将一个数组根据某个字符或字串进行分割成数组的时候,explode用的很happy,但是你知道~explode是怎么工作的么~~
首先可以肯定的是,explode也是会分配空间的,毫无疑问。
代码如下:
  1. <br>//文件1:ext/standard/string.c <br>//先来看下explode的源代码 <br>PHP_FUNCTION(explode) <br>{ <br>char *str, *delim; <br>int str_len = 0, delim_len = 0; <br>long limit = LONG_MAX; /* No limit */ <br>zval zdelim, zstr; <br>if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &delim, &delim_len, &str, &str_len, &limit) == FAILURE) { <br>return; <br>} <br>if (delim_len == 0) { <br>php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter"); <br>RETURN_FALSE; <br>} <br>//这里会开辟一个数组,用来存放分割后的数据 <br>array_init(return_value); <br>//因为这个,我们用explode('|', '');成为了合法的 <br>if (str_len == 0) { <br>if (limit >= 0) { <br>add_next_index_stringl(return_value, "", sizeof("") - 1, 1); <br>} <br>return; <br>} <br>//下面这两个是将原字串和分割符都构建成_zval_struct 结构, <br>//ZVAL_STRINGL会分配空间哦~~源代码随后贴出 <br>ZVAL_STRINGL(&zstr, str, str_len, 0); <br>ZVAL_STRINGL(&zdelim, delim, delim_len, 0); <br>//limit值是explode中允许传递的explode的第三个参数,它允许正负 <br>if (limit > 1) { <br>php_explode(&zdelim, &zstr, return_value, limit); <br>} else if (limit < 0) { <br>php_explode_negative_limit(&zdelim, &zstr, return_value, limit); <br>} else { <br>add_index_stringl(return_value, 0, str, str_len, 1); <br>} <br>} <br> <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br>//ZVAL_STRINGL的源代码: <br>//文件2:zend/zend_API.c <br>#define ZVAL_STRINGL(z, s, l, duplicate) { \ <br>const char *__s=(s); int __l=l; \ <br>Z_STRLEN_P(z) = __l; \ <br>Z_STRVAL_P(z) = (duplicate?estrndup(__s, __l):(char*)__s);\ <br>Z_TYPE_P(z) = IS_STRING; \ <br>} <br>.... <br>//estrndup才是主菜: <br>//文件3:zend/zend_alloc.h <br>#define estrndup(s, length) _estrndup((s), (length) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) <br>.... <br>//_estrndup的实现: zend/zend_alloc.c <br>ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) <br>{ <br>char *p; <br>p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); <br>if (UNEXPECTED(p == NULL)) { <br>return p; <br>} <br>memcpy(p, s, length); //分配空间 <br>p[length] = 0; <br>return p; <br>} <br>//另外在substr和strrchr strstr中用到的ZVAL_STRING也是使用了上诉的实现 <br> <br>下面根据explode的第三个参数limit来分析调用:条件对应的是explode中最后的三行,对limit条件的不同 <br>注: limit在缺省的时候(没有传递),他的默认值是LONG_MAX,也就是属于分支1的情况 <br>1、limit > 1 : <br>调用php_explode方法,该方法也可以在ext/standard/string.c中找到,并且是紧接着explode实现的上面出现(所以在查找本函数中调用来自本文件的方法的时候很方便,几乎无一列外都是在该函数的紧接着的上面^_^), <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br>PHPAPI void php_explode(zval *delim, zval *str, zval *return_value, long limit) <br>{ <br>char *p1, *p2, *endp; <br>//先得到的是源字串的末尾位置的指针 <br>endp = Z_STRVAL_P(str) + Z_STRLEN_P(str); <br>//记录开始位置 <br>p1 = Z_STRVAL_P(str); <br>//下面这个是获得分割符在str中的位置,可以看到在strrpos和strpos中也用到了这个方法去定位 <br>p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp); <br>if (p2 == NULL) { <br>//因为这个,所以当我们调用explode('|', 'abc');是合法的,出来的的就是array(0 => 'abc') <br>add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1); <br>} else { <br>//依次循环获得下一个分隔符的位置,直到结束 <br>do { <br>//将得到的子字串(上个位置到这个位置中间的一段,第一次的时候上个位置就是开始 <br>add_next_index_stringl(return_value, p1, p2 - p1, 1); <br>//定位到分隔符位置p2+分隔符的长度的位置 <br>//比如,分隔符='|', 原字串= 'ab|c', p2 = 2, 则p1=2+1=3 <br>p1 = p2 + Z_STRLEN_P(delim); <br>} while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL && <br>--limit > 1); <br>//将最后的一个分隔符后面的字串放到结果数组中 <br>//explode('|', 'avc|sdf'); => array(0 => 'avc', 1= > 'sdf') <br>if (p1 <= endp) <br>add_next_index_stringl(return_value, p1, endp-p1, 1); <br>} <br>} <br> <br>2、limit < 0 : <br>调用php_explode_negative_limit方法 <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br>PHPAPI void php_explode_negative_limit(zval *delim, zval *str, zval *return_value, long limit) <br>{ <br>#define EXPLODE_ALLOC_STEP 64 <br>char *p1, *p2, *endp; <br>endp = Z_STRVAL_P(str) + Z_STRLEN_P(str); <br>p1 = Z_STRVAL_P(str); <br>p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp); <br>if (p2 == NULL) { <br>//它这里竟然没有处理,那explode('|', 'abc', -1) 就成非法的了,获得不了任何值 <br>/* <br>do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0 <br>by doing nothing we return empty array <br>*/ <br>} else { <br>int allocated = EXPLODE_ALLOC_STEP, found = 0; <br>long i, to_return; <br>char **positions = emalloc(allocated * sizeof(char *)); <br>//注意这里的positions的声明,这个数组是用来保存所有子字串的读取位置 <br>positions[found++] = p1; //当然起始位置还是需要保存 <br>//下面两个循环,第一个是循环所有在字符串中出现的分隔符位置,并保存下一个子字串读取位置起来 <br>do { <br>if (found >= allocated) { <br>allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */ <br>positions = erealloc(positions, allocated*sizeof(char *)); <br>} <br>positions[found++] = p1 = p2 + Z_STRLEN_P(delim); <br>} while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL); <br>//这个就是从数组中开始获得返回的结果将从哪个子字串开始读 <br>to_return = limit + found; <br>/* limit is at least -1 therefore no need of bounds checking : i will be always less than found */ <br>for (i = 0;i < to_return;i++) { /* this checks also for to_return > 0 */ <br>add_next_index_stringl(return_value, positions[i], <br>(positions[i+1] - Z_STRLEN_P(delim)) - positions[i], <br>1 <br>); <br>} <br>efree(positions);//很重要,释放内存 <br>} <br>#undef EXPLODE_ALLOC_STEP <br>} <br> <br>3、limit = 1 or limit = 0 : <br>当所有第一和第二条件都不满足的时候,就进入的这个分支,这个分支很简单就是将源字串放到</li></ol></pre>输出数组中,explode('|', 'avc|sd', 1) or explode('|', 'avc|sd', 0) 都将返回array(0 => 'avc|sd'); <br><span><u></u></span> 代码如下:<pre class="brush:php;toolbar:false layui-box layui-code-view layui-code-notepad"><ol class="layui-code-ol"><li><br>//add_index_stringl源代码 <br>//文件4:zend/zend_API.c <br>ZEND_API int add_next_index_stringl(zval *arg, const char *str, uint length, int duplicate) /* {{{ */ <br>{ <br>zval *tmp; <br>MAKE_STD_ZVAL(tmp); <br>ZVAL_STRINGL(tmp, str, length, duplicate); <br>return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp, sizeof(zval *), NULL); <br>} <br>//zend_hash_next_index_insert <br>//zend/zend_hash.h <br>#define zend_hash_next_index_insert(ht, pData, nDataSize, pDest) \ <br>_zend_hash_index_update_or_next_insert(ht, 0, pData, nDataSize, pDest, HASH_NEXT_INSERT ZEND_FILE_LINE_CC) <br>//zend/zend_hash.c <br>///太长了~~~~不贴了 <br> <br>可见(不包含分配空间这些), <br>当limit>1的时候,效率是O(N)【N为limit值】, <br>当limit<0的时候,效率是O(N+M)【N为limit值, M 为分割符出现次数】, <br>当limit=1 or limit=0 的时候, 效率是O(1) </li></ol></pre></li></ol></pre></li></ol></pre>

人气教程排行