今天和同事在讨论关键字过虑的算法实现,前几天刚看过布隆过滤算法,于是就想起我们公司内部的查找关键字程序,好奇是怎么实现的。于是查找了一下源代码,原来可以简单地用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' ) {
//命令行模式
global $ argv ;
$ function = $ argv [ 1] ;
} else {
//网页方式
$ function = $ _GET [ 'name' ] ;
}
$ extensions = get_loaded_extensions ( ) ;
foreach ( $ extensions as $ t ) {
$ modules_funcs = get_extension_funcs ( $ t ) ;
if ( in_array ( $ function , ( array ) $ modules_funcs ) ) {
$ is_found = true ;
echo "$function is in $t module\n" ;
}
}
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这个函数实现的,只是在返回的时候内容不同而已。
运维网声明
1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com