程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP基礎知識 >> PHP源代碼分析-字符串搜索系列函數實現詳解

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'){
&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這個函數實現的,只是在返回的時候內容不同而已。

 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved