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 相关文章推荐
模拟OICQ的实现思路和核心程序(二)
Oct 09 PHP
php 文件上传系统手记
Oct 26 PHP
PHP数组循环操作详细介绍 附实例代码
Feb 03 PHP
解析argc argv在php中的应用
Jun 24 PHP
关于js和php对url编码的处理方法
Mar 04 PHP
php foreach正序倒序输出示例代码
Jul 01 PHP
微信自定义菜单的创建/查询/取消php示例代码
Aug 05 PHP
php抽象方法和抽象类实例分析
Dec 07 PHP
thinkPHP框架可添加js事件的分页类customPage.class.php完整实例
Mar 16 PHP
PHP 进度条函数的简单实例
Sep 19 PHP
PHP实现验证码校验功能
Nov 16 PHP
PHP实现的文件浏览器功能简单示例
Sep 12 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
德生PL330的评价与改造
2021/03/02 无线电
PHP中使用mktime获取时间戳的一个黑色幽默分析
2012/05/31 PHP
使用xampp搭建运行php虚拟主机的详细步骤
2015/10/21 PHP
thinkPHP统计排行与分页显示功能示例
2016/12/02 PHP
PHP删除数组中指定值的元素常用方法实例分析【4种方法】
2018/08/21 PHP
微信推送功能实现方式图文详解
2019/07/12 PHP
php回调函数处理数组操作示例
2020/04/13 PHP
js apply/call/caller/callee/bind使用方法与区别分析
2009/10/28 Javascript
jquery表单验证框架提供的身份证验证方法(示例代码)
2013/12/27 Javascript
关闭时刷新父窗口两种方法
2014/05/07 Javascript
5个数组Array方法: indexOf、filter、forEach、map、reduce使用实例
2015/01/29 Javascript
使用AngularJS处理单选框和复选框的简单方法
2015/06/19 Javascript
多个js毫秒倒计时同时进行效果
2016/01/05 Javascript
原生js实现autocomplete插件
2016/04/14 Javascript
详解Vue 普通对象数据更新与 file 对象数据更新
2017/04/26 Javascript
vue2.0使用swiper组件实现轮播效果
2017/11/27 Javascript
vue自定义switch开关组件,实现样式可自行更改
2019/11/01 Javascript
基于PHP pthreads实现多线程代码实例
2020/06/24 Javascript
[03:24]CDEC.Y赛前采访 努力备战2016国际邀请赛中国区预选赛
2016/06/25 DOTA
在Python中使用全局日志时需要注意的问题
2015/05/06 Python
Python中查看文件名和文件路径
2017/03/31 Python
分享一个简单的python读写文件脚本
2017/11/25 Python
Django1.9 加载通过ImageField上传的图片方法
2018/05/25 Python
python实现批量修改图片格式和尺寸
2018/06/07 Python
Python实现DDos攻击实例详解
2019/02/02 Python
Python线程之定位与销毁的实现
2019/02/17 Python
django 邮件发送模块smtp使用详解
2019/07/22 Python
Python网页解析器使用实例详解
2020/05/30 Python
Python参数传递及收集机制原理解析
2020/06/05 Python
Python基于time模块表示时间常用方法
2020/06/18 Python
用于ETL的Python数据转换工具详解
2020/07/21 Python
仓管岗位职责范本
2014/02/08 职场文书
《去年的树》教学反思
2014/04/11 职场文书
公司贷款承诺书
2014/05/30 职场文书
红领巾广播站广播稿(3篇)
2014/09/20 职场文书
忠犬八公的故事观后感
2015/06/05 职场文书