PHP中explode的使用 新手教程

  今天小编给大家分享一篇php.html" target="_blank">PHP中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

  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

  add_next_index_stringl(return_value, p1, endp-p1, 1);

  }

  }

  2、limit

  调用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

  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 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

  当limit=1 or limit=0 的时候, 效率是O(1)

  以上就是关于PHP中explode的使用 新手教程,大家可以慢慢学习一下哦,更多相关内容请继续关注中国E盟技术频道。