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 相关文章推荐
PHP脚本的10个技巧(3)
Oct 09 PHP
我的论坛源代码(九)
Oct 09 PHP
php桌面中心(二) 数据库写入
Mar 11 PHP
提高PHP编程效率 引入缓存机制提升性能
Feb 15 PHP
php中XMLHttpRequest(Ajax)不能设置自定义的Referer的解决方法
Nov 26 PHP
php使用unset()删除数组中某个单元(键)的方法
Feb 17 PHP
PHP多线程编程之管道通信实例分析
Mar 07 PHP
PHP实现的增强性mhash函数
May 27 PHP
php面向对象与面向过程两种方法给图片添加文字水印
Aug 26 PHP
php如何实现只替换一次或N次
Oct 29 PHP
php微信开发接入
Aug 27 PHP
Laravel 简单实现Ajax滚动加载示例
Oct 22 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和MySql来与ODBC数据连接
2006/10/09 PHP
PHP程序员编程注意事项
2008/04/10 PHP
解析php中如何调用用户自定义函数
2013/08/06 PHP
php设置session值和cookies的学习示例
2014/03/21 PHP
destoon常用的安全设置概述
2014/06/21 PHP
php+Mysqli利用事务处理转账问题实例
2015/02/11 PHP
PHP判断FORM表单或URL参数来的数据是否为整数的方法
2016/03/25 PHP
php文件操作之文件写入字符串、数组的方法分析
2019/04/15 PHP
PHP实现字符串的全排列详解
2019/04/24 PHP
PHP使用DOM对XML解析处理操作示例
2019/07/04 PHP
PHP实现批量修改文件名的方法示例
2019/09/18 PHP
jquery 操作单选框,复选框,下拉列表实现代码
2009/10/27 Javascript
date.parse在IE和FF中的区别
2010/07/29 Javascript
为jQuery.Treeview添加右键菜单的实现代码
2010/10/22 Javascript
详解AngularJS中自定义指令的使用
2015/06/17 Javascript
浅谈javascript的Array.prototype.slice.call
2015/08/31 Javascript
浅谈JavaScript函数的四种存在形态
2016/06/08 Javascript
AngularJS ng-change 指令的详解及简单实例
2016/07/30 Javascript
原生js仿jquery一些常用方法(必看篇)
2016/09/20 Javascript
JS实现的检验身份证格式并输出出生日期,年龄,性别,出生地示例
2019/05/17 Javascript
JavaScript事件冒泡机制原理实例解析
2020/01/14 Javascript
[48:29]2018DOTA2亚洲邀请赛3月30日 小组赛A组 LGD VS KG
2018/03/31 DOTA
[44:40]Serenity vs Pain 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
基于python绘制科赫雪花
2018/06/22 Python
解决Django中调用keras的模型出现的问题
2019/08/07 Python
通过Turtle库在Python中绘制一个鼠年福鼠
2020/02/03 Python
经济管理专业毕业生推荐信
2013/11/11 职场文书
求职个人评价范文
2014/04/09 职场文书
捐款倡议书格式范文
2014/05/14 职场文书
机械专业技术员求职信
2014/06/14 职场文书
党支部三会一课计划
2014/09/24 职场文书
深入浅析Redis 集群伸缩原理
2021/05/15 Redis
如何用PHP实现多线程编程
2021/05/26 PHP
升级 Win11 还是坚守 Win10?微软 Win11 新系统缺失功能大盘点
2022/04/05 数码科技
CentOS7和8下安装Maven3.8.4
2022/04/07 Servers
如何利用python创作字符画
2022/06/25 Python