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数组实例总结与说明
Aug 23 PHP
需要注意的几个PHP漏洞小结
Feb 05 PHP
如何使用PHP实现javascript的escape和unescape函数
Jun 29 PHP
分享常见的几种页面静态化的方法
Jan 08 PHP
php通过array_shift()函数移除数组第一个元素的方法
Mar 18 PHP
php上传图片类及用法示例
May 11 PHP
php连接oracle数据库的核心步骤
May 26 PHP
php源码 fsockopen获取网页内容实例详解
Sep 24 PHP
thinkPHP实现签到功能的方法
Mar 15 PHP
Laravel源码解析之路由的使用和示例详解
Sep 27 PHP
PHP使用POP3读取邮箱接收邮件的示例代码
Jul 08 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
WAR3重制版DOTA 5V5初体验
2020/04/09 DOTA
解析isset与is_null的区别
2013/08/09 PHP
thinkphp视图模型查询提示ERR: 1146:Table 'db.pr_order_view' doesn't exist的解决方法
2014/10/30 PHP
功能强大的PHP图片处理类(水印、透明度、旋转)
2015/10/21 PHP
php获取客户端IP及URL的方法示例
2017/02/03 PHP
PHP+Ajax简单get验证操作示例
2019/03/02 PHP
use jscript Create a SQL Server database
2007/06/16 Javascript
Flash+XML滚动新闻代码 无图片 附源码下载
2007/11/22 Javascript
JavaScript中的事件处理
2008/01/16 Javascript
jQuery选择器querySelector的使用指南
2015/01/23 Javascript
JS+CSS实现淡入式焦点图片幻灯切换效果的方法
2015/02/26 Javascript
基于Vue实现tab栏切换内容不断实时刷新数据功能
2017/04/13 Javascript
nodejs+websocket实时聊天系统改进版
2017/05/18 NodeJs
bootstrapvalidator之API学习教程
2017/06/29 Javascript
javascript实现获取一个日期段内每天不同的价格(计算入住总价格)
2018/02/05 Javascript
Angular 4.x+Ionic3踩坑之Ionic3.x pop反向传值详解
2018/03/13 Javascript
vue列表数据发生变化指令没有更新问题及解决方法
2020/01/16 Javascript
解决vue刷新页面以后丢失store的数据问题
2020/08/11 Javascript
Javascript实现贪吃蛇小游戏(含详细注释)
2020/10/23 Javascript
python实现挑选出来100以内的质数
2015/03/24 Python
python通过自定义isnumber函数判断字符串是否为数字的方法
2015/04/23 Python
Android应用开发中Action bar编写的入门教程
2016/02/26 Python
Windows中安装使用Virtualenv来创建独立Python环境
2016/05/31 Python
CentOS中升级Python版本的方法详解
2017/07/10 Python
python实现决策树
2017/12/21 Python
Python绘制堆叠柱状图的实例
2019/07/09 Python
python实现多线程端口扫描
2019/08/31 Python
Python编程快速上手——strip()函数的正则表达式实现方法分析
2020/02/29 Python
Django使用django-simple-captcha做验证码的实现示例
2021/01/07 Python
Python之多进程与多线程的使用
2021/02/23 Python
全球性的在线时尚男装零售商:boohooMAN
2016/12/17 全球购物
澳大利亚儿童和婴儿产品在线商店:Lime Tree Kids
2017/10/05 全球购物
Gloeilampgoedkoop荷兰:在线购买灯泡
2019/02/16 全球购物
运动会横幅标语
2014/06/17 职场文书
社区创先争优承诺书
2014/08/30 职场文书
PyQt5结合QtDesigner实现文本框读写操作
2021/06/11 Python