`
tiandirensoon
  • 浏览: 596855 次
文章分类
社区版块
存档分类
最新评论

PHP源代码分析-字符串搜索系列函数实现详解

 
阅读更多

今天和同事在讨论关键字过虑的算法实现,前几天刚看过布隆过滤算法,于是就想起我们公司内部的查找关键字程序,好奇是怎么实现的。于是查找了一下源代码,原来可以简单地用stripos函数查找,
stripos原型如下:
int stripos ( string $haystack, string $needle [, int $offset] )
一般地都会建一个关键词库,然后把用户输入的内容作为haystack,然后循环遍历一下关键词库,把每个关键词作为needle,如果存在的话则会返回关键字在输入的内容中的位置。
于是查找了一下PHP源代码关于这个函数的实现,如果想知道一个函数在PHP的哪个模块的话可以简单写一个函数get_module.php
<?php

if(substr(php_sapi_name(),0,6)=='cli'){
&nbsp;&nbsp;&nbsp;&nbsp;//命令行模式

&nbsp;&nbsp;&nbsp;&nbsp;global $argv;
&nbsp;&nbsp;&nbsp;&nbsp;$function = $argv[1];
}else{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//网页方式

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$function = $_GET['name'];
}
$extensions = get_loaded_extensions();
foreach($extensions as $t){
&nbsp;&nbsp;&nbsp;&nbsp;$modules_funcs = get_extension_funcs($t);
&nbsp;&nbsp;&nbsp;&nbsp;if(in_array($function, (array)$modules_funcs)){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$is_found = true;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "$function is in $t module\n";
&nbsp;&nbsp;&nbsp;&nbsp;}
}
if(!$is_found)echo "$function not found";

?>


字符串系列的函数属于PHP的标准模块,在ext/standard目录下,string.c文件

PHP_FUNCTION(stripos)
{
char *found = NULL;
char *haystack;
int haystack_len;
long offset = 0;
char *needle_dup = NULL, *haystack_dup;
char needle_char[2];
zval *needle;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &needle, &offset) == FAILURE) {
return;
}
检查参数,第一第二个是必选参数,第三个是可选,|表示后面的参数都是可选的。

if (offset < 0 || offset > haystack_len) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
}

if (haystack_len == 0) {
RETURN_FALSE;
}

haystack_dup = estrndup(haystack, haystack_len);
//与大小写无关,所以先把字符全部转换成小写

php_strtolower(haystack_dup, haystack_len);

if (Z_TYPE_P(needle) == IS_STRING) {
//第二个参数如果是字符串

if (Z_STRLEN_P(needle) == 0 || Z_STRLEN_P(needle) > haystack_len) {
efree(haystack_dup);
RETURN_FALSE;
}

needle_dup = estrndup(Z_STRVAL_P(needle), Z_STRLEN_P(needle));
php_strtolower(needle_dup, Z_STRLEN_P(needle));
//这个是关键,由php_memnstr实现

found = php_memnstr(haystack_dup + offset, needle_dup, Z_STRLEN_P(needle), haystack_dup + haystack_len);
} else {
//第二个参数是数字的话,则强制转换成(char)类型

switch (Z_TYPE_P(needle)) {
case IS_LONG:
case IS_BOOL:
needle_char[0] = tolower((char) Z_LVAL_P(needle));
break;
case IS_DOUBLE:
needle_char[0] = tolower((char) Z_DVAL_P(needle));
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "needle is not a string or an integer");
efree(haystack_dup);
RETURN_FALSE;
break;

}
needle_char[1] = '\0';
found = php_memnstr(haystack_dup + offset,
needle_char,
sizeof(needle_char) - 1,
haystack_dup + haystack_len);
}

efree(haystack_dup);
if (needle_dup) {
efree(needle_dup);
}

if (found) {
//如何找到,则返回在这个字符串中的位置

RETURN_LONG(found - haystack_dup);
} else {
RETURN_FALSE;
}
}


查找函数是由php_memstr实现的,在main目录下的php.h文件
#define php_memnstr zend_memnstr

所以真正的函数是zend_memnstr,在zend/目录下面的zend_operators.h,

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

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


查到这里就能看到实现搜索的原理了,主要用了一个while循环和两个C的函数memchr和memcmp。
先用第一个函数查找needle的第一个字符在haystack中出现的位置,然后调用memcmp,从这个位置开始比较needle和haystack,如果相同就返回这个位置,没有的话再把指针指向haystack的下一位再进行比较,一直到最后。
不过这个搜索只是简单地调用了memchr和memcmp函数,至于memcmp用了什么算法比较两个字符串就不太清楚,我们知道在一个长度为n的字符串里面查找字符串为m的字符串,那么最坏的时间复杂度是O(n*m),上网搜了一下memcmp,不过没有找到他的实现原理。后来想了一下发现这个其实就是最简单的两次循环遍历进行比较。看了一下PHP的其他几个字符串查找函数strstr,stristr,strpos,strrpos,strripos 等函数都是调用zend_memnstr这个函数实现的,只是在返回的时候内容不同而已。

分享到:
评论

相关推荐

    c语言字符串函数详解--函数名及源代码整理

    c语言字符串函数详解--函数名及源代码整理

    c语言字符串函数详解--函数名及源代码整理.pdf

    c语言字符串函数详解--函数名及源代码整理.pdf

    c语言字符串函数详解--函数名及源代码整理[借鉴].pdf

    c语言字符串函数详解--函数名及源代码整理[借鉴].pdf

    Linux C字符串替换函数实例详解

    Linux C字符串替换函数实例详解  最近学习linux 的基础编程知识,字符串替换函数,在网上找下资料,觉得这篇文章写的不错,记录下来,和大家分享一下: 实例代码: #include #include #include /** * * @author...

    基于C++字符串替换函数的使用详解

    这里主要说一下STL里的WString中的替换,虽然WString自带了一个Replace函数,但是只能替换一次,太不好了,因此单独写了个替换函数[函数] 代码如下:/** * @brief 实现字符串替换 * @param orignStr 源串 ...

    基于PHP字符串的比较函数strcmp()与strcasecmp()的使用详解

    本篇文章是对PHP字符串的比较函数strcmp()与strcasecmp()的使用进行了详细的分析介绍,需要的朋友参考下

    vc源代码合集0951.rar

    2012-06-12 12:22 10,537 C和C++字符串处理函数.txt 2012-06-12 12:21 8,825 c扫描器源码.txt 2012-06-12 12:39 505,110 c语言也能干大事全部板书(带书签)-感谢rupeng.com鹏友的整理.rar 2012-06-12 12:10 183,001 ...

    jQuery权威指南-源代码

    9.5 综合案例分析—使用jQuery扩展工具函数实现对字符串指定类型的检测/305 9.5.1 需求分析/305 9.5.2 效果界面/305 9.5.3 功能实现/306 9.5.4 代码分析/309 9.6 本章小结/311 第10章 jQuery性能优化与最佳...

    【JavaScript源代码】JavaScript parseInt()与Number()区别案例详解.docx

     学习目标: parseInt()、Number()这两个函数用到最多的地方就是把一个字符串转换成数据类型,那么他们都有哪些区别? 学习内容: parseInt()函数将给定的字符串以指定的基数解析为整数。 parseInt(string,...

    面向对象技术与UML课件及源代码-by 南邮-陈杨

    本书提供了所有实例的源代码以及开发过程中用到的软件下载地址,供读者学习参考使用。 本书为学校教学量身定做,供高校面向对象技术相关课程使用,对于缺乏项目实战经验的程序员来说可用于快速积累项目开发经验。 ...

    VB串口通信源码210个

    003、Visual Basic串口通信与测控应用技术实战详解 源代码(15个全) 004、GE PLC串口通讯,VB编制,读取内存单元 005、PC机与51单片机之间的串口通讯,VB编的,分PC和单片机两部分 006、VB6的串口通信程序,还有crc校验 ...

    Visual Basic 数据采集与串口通信测控应用实战(part1)

    2.4.2 字符串函数 34 2.4.3 日期与时间函数 35 2.4.4 转换函数 35 2.4.5 判断函数 36 2.4.6 颜色设置函数 36 2.4.7 字符串的处理 38 2.5 vb用户界面设计 39 2.5.1 内部控件 39 2.5.2 ...

    Visual Basic 数据采集与串口通信测控应用实战(part2)

    2.4.2 字符串函数 34 2.4.3 日期与时间函数 35 2.4.4 转换函数 35 2.4.5 判断函数 36 2.4.6 颜色设置函数 36 2.4.7 字符串的处理 38 2.5 vb用户界面设计 39 2.5.1 内部控件 39 2.5.2 ...

    Microsoft+SQL+Server+2008技术内幕:T-SQL查询_源代码及附录 中文版

    5.3.2 字符串查找225 5.4 一个实际的应用程序226 5.4.1 识别测量数据的趋势226 5.4.2 LISLP算法的复杂度226 5.4.3 用T-SQL解决最长上升子序列的长度问题227 5.5 总结229 第6章 子查询、表表达式和排名函数231 ...

    【JavaScript源代码】JavaScript中正则表达式的实际应用详解.docx

    JavaScript中正则表达式的实际应用详解  实际工作中,JavaScript正则表达式还是经常用到的。所以这部分的知识是非常重要的... 其中的flags有3个标志 g:表示全局模式,即模式将被应用于所有字符串,而非在发现第一个

    TCP/IP详解 (卷2:实现)

     然后我们介绍一个简单的用户程序,它发送一个UDP数据报给一个位于另一主机上的日期,时间服务器,服务器返回一个UDP数据报,其中包含服务器上日期和时间的ASCIl码字符串。这个进程发送的数据报经过所有的协议栈...

    C程序范例宝典(基础代码详解)

    实例074 使用指针实现字符串复制 92 实例075 字符串的连接 94 实例076 字符串插入 95 实例077 字符串的匹配 96 2.5 指向指针的指针 97 实例078 使用指针的指针输出字符串 98 实例079 实现输入月份号...

    基于C语言string函数的详解

    @函数原型: char *strdup(const char *s) 函数功能: 字符串拷贝,目的空间由该函数分配 函数返回: 指向拷贝后的字符串指针 参数说明: src-待拷贝的源字符串 所属文件: &lt;string&gt; 代码如下:#include &lt;stdio&gt; #...

    md格式编写的良心教程 Python 100天从新手到大师 共100个完整源文件 含课程源代码.rar

    字符串和常用数据结构.md Day01-15\08.面向对象编程基础.md Day01-15\09.面向对象进阶.md Day01-15\10.图形用户界面和游戏开发.md Day01-15\11.文件和异常.md Day01-15\12.字符串和正则表达式.md Day01-15\13.进程和...

    基于Python的文件类型和字符串详解

    1. 源代码–直接由Python解析 vi 1.py #!/usr/bin/python print 'hello world' 这里的1.py就是源代码 执行方式和shell脚本类似: chmod +x 后,./1.py Python 1.py 2. 字节代码 Python源码文件经编译后生成的扩展...

Global site tag (gtag.js) - Google Analytics