PHP中strpos、strstr和stripos、stristr函数分析


Posted in PHP onJune 11, 2016

本文为大家分析了 PHP中strpos、strstr和stripos、stristr函数,供大家参考,具体内容如下

strpos

mixed strpos ( string $haystack, mixed $needle [, int $offset = 0 ] )
如果offset指定了,查找会从offset的位置开始。offset不能为负数。

返回needle第一次出现在haystack的位置。如果在haystack中找不到needle,则返回FALSE。

needle,如果needle不是字符串,它会被转换成整型数值并赋值为该数值的ASCII字符。请看下面例子。

例子

$str = "hello";
$pos = strpos($str, 111);
// 111的ASCII值是o,因此$pos = 4
strpos核心源码

if (Z_TYPE_P(needle) == IS_STRING) {
   if (!Z_STRLEN_P(needle)) {
     php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty needle");
     RETURN_FALSE;
   }

   // 调用php_memnstr函数查找needle
   found = php_memnstr(haystack + offset,
              Z_STRVAL_P(needle),
              Z_STRLEN_P(needle),
              haystack + haystack_len);
   } else {
     // 如果不是字符串,转换成数字并赋值为该数字的ASCII字符。
     if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
        RETURN_FALSE;
     }
     //设置结束字符
     needle_char[1] = 0;
     found = php_memnstr(haystack + offset,
              needle_char,
              1,
              haystack + haystack_len);
 }
}

有一点要注意的是,如果needle不是字符串的话,会调用php_needle_char函数将needle转成整型数字并转换为其ASCII值。

查找函数

函数最后返回的是found,php_memnstr函数实现了查找的方法。那么再继续看看php_memnstr函数做了什么:

#define php_memnstr zend_memnstr
php_memnstr是函数zend_memnstr的宏定义,查看zend_memnstr函数如下:

static inline char *
zend_memnstr(char *haystack, char *needle, int needle_len, char *end)
{
  char *p = haystack;
  char ne = needle[needle_len-1];
  if (needle_len == 1) {
    return (char *)memchr(p, *needle, (end-p));
  }

  if (needle_len > end-haystack) {
    return NULL;
  }

  // 第一个优化,只查找end - needle_len次
  end -= needle_len;

  while (p <= end) {
    // 第二个优化,先判断字符串的开头和结尾是否一样再判断整个字符串
    if ((p = (char *)memchr(p, *needle, (end-p+1))) && ne == p[needle_len-1]) {
      if (!memcmp(needle, p, needle_len-1)) {
        return p;
      }
    }

    if (p == NULL) {
      return NULL;
    }

    p++;
  }

  return NULL;
}

第一个优化,因为(char *)memchr(p, *needle, (end-p+1)是在end ? needle_len + 1(即haystack_len+1)中查找,如果p为空,说明needle的第一个字符在p中从未出现过。

strstr

string strstr ( string $haystack, mixed $needle [, bool $before_needle = false ] )

返回needle在haystack中第一次出现的位置到结束的字符串。
这个函数的区分大小写的。

如果needle在haystack中不存在,返回FALSE。

如果before_needle为true,则返回haystack中needle在haystack第一次出现的位置之前的字符串。

strstr核心源码

if (found) {
    // 计算出found的位置
    found_offset = found - haystack;
    if (part) {
      RETURN_STRINGL(haystack, found_offset, 1);
    } else {
      RETURN_STRINGL(found, haystack_len - found_offset, 1);
    }
}

strstr函数的前半部分跟strpos类似,区别在于strstr函数在找到位置后,需要返回haystack部分的字符串。part变量就是调用strstr函数时传递的before_needle变量。

stripos

mixed stripos ( string $haystack, string $needle [, int $offset = 0 ] )

不区分大小写的strpos。实现方式跟下面的类似,主要是使用一份拷贝然后将需要比较的字符串转换成小写字符后进行再进行查找。

stristr

string stristr ( string $haystack, mixed $needle [, bool $before_needle = false ] ) 不区分大小写的strstr。

核心源码

// 拷贝一份haystack
haystack_dup = estrndup(haystack, haystack_len);

if (Z_TYPE_P(needle) == IS_STRING) {
  char *orig_needle;
  if (!Z_STRLEN_P(needle)) {
    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty needle");
    efree(haystack_dup);
    RETURN_FALSE;
  }
  orig_needle = estrndup(Z_STRVAL_P(needle), Z_STRLEN_P(needle));
  // 调用php_stristr函数找出orig_needle的值。
  found = php_stristr(haystack_dup, orig_needle,  haystack_len, Z_STRLEN_P(needle));
  efree(orig_needle);
} else {
  if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
    efree(haystack_dup);
    RETURN_FALSE;
  }
  needle_char[1] = 0;

  found = php_stristr(haystack_dup, needle_char,  haystack_len, 1);
}

if (found) {
  found_offset = found - haystack_dup;
  if (part) {
    RETVAL_STRINGL(haystack, found_offset, 1);
  } else {
    RETVAL_STRINGL(haystack + found_offset, haystack_len - found_offset, 1);
  }
} else {
  RETVAL_FALSE;
}

// 释放变量
efree(haystack_dup);

可以知道,found是从php_stristr中得到的,继续查看php_stristr函数:

PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len)
{
  php_strtolower(s, s_len);
  php_strtolower(t, t_len);
  return php_memnstr(s, t, t_len, s + s_len);
}

这个函数的功能就是将字符串都转成小写之后调用php_mennstr函数来查找needle在haystack第一次出现的位置。

总结

因为strpos/stripos返回的是位置,位置从0开始计算,所以判断查找失败都用=== FALSE更适合。

阅读PHP的源码收获挺多,一方面可以知道某个函数的具体实现原理是怎样的,另一方面可以学习到一些编程优化方案。

以上就是本文的全部内容,希望对大家学习php程序设计有所帮助。

PHP 相关文章推荐
人大复印资料处理程序_输入篇
Oct 09 PHP
PHP 中执行排序与 MySQL 中排序
Apr 21 PHP
PHP设计模式之代理模式的深入解析
Jun 13 PHP
解析thinkphp中的导入文件标签
Jun 20 PHP
php判断正常访问和外部访问的示例
Feb 10 PHP
2款PHP无限级分类实例代码
Nov 11 PHP
php验证手机号码
Nov 11 PHP
PHP面向对象程序设计之命名空间与自动加载类详解
Dec 02 PHP
PHP实现阿里大鱼短信验证的实例代码
Jul 10 PHP
详解PHP队列的实现
Mar 14 PHP
PHP实现15位身份证号转18位的方法分析
Oct 16 PHP
PHP如何使用JWT做Api接口身份认证的实现
Feb 03 PHP
linux下php上传文件注意事项
Jun 11 #PHP
php设计模式之单例模式代码
Jun 11 #PHP
浅谈PHP Cookie处理函数
Jun 10 #PHP
php单例模式的简单实现方法
Jun 10 #PHP
PHP操作mysql数据库分表的方法
Jun 09 #PHP
浅谈PHP链表数据结构(单链表)
Jun 08 #PHP
PHP Yaf框架的简单安装使用教程(推荐)
Jun 08 #PHP
You might like
PHP实现检测客户端是否使用代理服务器及其匿名级别
2015/01/07 PHP
Ubuntu下安装PHP的mongodb扩展操作命令
2015/07/04 PHP
PHP导出Excel实例讲解
2016/01/24 PHP
php魔术方法功能与用法实例分析
2016/10/19 PHP
php reset() 函数指针指向数组中的第一个元素并输出实例代码
2016/11/21 PHP
jQuery UI的Dialog无法提交问题的解决方法
2011/01/11 Javascript
7款风格新颖的jQuery/CSS3菜单导航分享
2013/04/23 Javascript
JavaScript中逗号运算符介绍及使用示例
2015/03/13 Javascript
jQuery弹出层插件Lightbox_me使用指南
2015/04/21 Javascript
JavaScript中计算网页中某个元素的位置
2015/06/10 Javascript
javascript使用输出语句实现网页特效代码
2015/08/06 Javascript
js实现显示当前状态的导航效果代码
2015/08/28 Javascript
JS实现的竖向折叠菜单代码
2015/10/21 Javascript
对比分析Django的Q查询及AngularJS的Datatables分页插件
2017/02/07 Javascript
js仿淘宝评价评分功能
2017/02/28 Javascript
AngularJS获取json数据的方法详解
2017/05/27 Javascript
vue :src 文件路径错误问题的解决方法
2018/05/15 Javascript
javascript 内存模型实例详解
2020/04/18 Javascript
详解JSON.stringify()的5个秘密特性
2020/05/26 Javascript
浅谈vue中$event理解和框架中在包含默认值外传参
2020/08/07 Javascript
使用pandas对两个dataframe进行join的实例
2018/06/08 Python
Keras SGD 随机梯度下降优化器参数设置方式
2020/06/19 Python
详解CSS3 用border写 空心三角箭头 (两种写法)
2017/09/29 HTML / CSS
Html5移动端适配IphoneX等机型的方法
2019/06/25 HTML / CSS
Html5 new XMLHttpRequest()监听附件上传进度
2021/01/14 HTML / CSS
一套软件测试笔试题
2014/07/25 面试题
自我推荐书
2013/12/04 职场文书
浙江文明网签名寄语
2014/01/18 职场文书
《放飞蜻蜓》教学反思
2014/04/27 职场文书
数控机床专业自荐信
2014/05/19 职场文书
班级出游活动计划书
2014/08/15 职场文书
2014年科室工作总结
2014/11/20 职场文书
教师先进个人材料
2014/12/17 职场文书
2014年污水处理厂工作总结
2014/12/19 职场文书
武当山导游词
2015/02/03 职场文书
Python代码,能玩30多款童年游戏!这些有几个是你玩过的
2021/04/27 Python