PHP源代码分析-字符串搜索系列函数实现详解
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                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'){     //命令行模式     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這個函數實現的,只是在返回的時候內容不同而已。
總結
以上是生活随笔為你收集整理的PHP源代码分析-字符串搜索系列函数实现详解的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 白脸是谁唱的啊?
- 下一篇: 深入理解PHP之OpCode
