[PHP源碼閱讀]explode和implode函數,explodeimplode_PHP教程

來源:互聯網
上載者:User

[PHP源碼閱讀]explode和implode函數,explodeimplode


explode和implode函數主要用作字串-數組的操作,比如擷取一段參數後根據某個字元分割字串,或者將一個數組的結果使用一個字元合并成一個字串輸出。在PHP中經常會用到這兩個函數,因此有必要瞭解一下其原理。

explode

array explode ( string $delimiter, string $string, [ , $limit ] )

返回由字串組成的數組,每個元素都是string的一個子串,被字串$delimiter作為邊界點分割出來。

$limit

如果設定了$limit,且為正數,則返回的數組最多包含$limit個元素,最後的那個元素將包含$string的剩餘部分。

如果$limit是負數,則返回除了最後的-$limit個元素外的所有元素。

如果$limit是0,則會被當做1。

$delimiter

如果$delimiter為空白,則函數返回FALSE。如果delimiter不在string中,且$limit為負數,則返回空數組。

核心源碼

    // 如果delimiter為空白字串,返回FALSE    if (delim_len == 0) {        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");        RETURN_FALSE;    }    // 初始化返回的數組    array_init(return_value);    if (str_len == 0) {          if (limit >= 0) {              // 如果字串為空白且limit大於等於0,則返回一個包含Null 字元串的數組,注意此處sizeof("") == 1            add_next_index_stringl(return_value, "", sizeof("") - 1, 1);        }        return;    }    // 初始化zstr和zdelim的字串變數    ZVAL_STRINGL(&zstr, str, str_len, 0);    ZVAL_STRINGL(&zdelim, delim, delim_len, 0);    if (limit > 1) {        // limit大於1,limit預設是LONG_MAX        php_explode(&zdelim, &zstr, return_value, limit);    } else if (limit < 0) {        // limit 為負數        php_explode_negative_limit(&zdelim, &zstr, return_value, limit);    } else {        // limit為0,被當作1處理,返回整個字串,add_index_stringl函數將str追加到數組return_value中        add_index_stringl(return_value, 0, str, str_len, 1);    }

處理完特殊情況和初始設定變數後,就調用php_explode/php_explode_negative_limit函數進行下一步處理。下面是php_explode函數的源碼

php_explode

  endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);    // p1指向字串的開始    p1 = Z_STRVAL_P(str);    // p2指向第一個分隔字元的位置 ,找出分隔字元位置主要用的是php_memnstr函數    p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);    if (p2 == NULL) {        // p2為NULL表示找不到分隔字元,直接返回整個字串        add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1);    } else {        do {            // 將p1添加到return_value數組中 ,移動到下一個分隔字元的位置            add_next_index_stringl(return_value, p1, p2 - p1, 1);            p1 = p2 + Z_STRLEN_P(delim);        } while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL &&                 --limit > 1);        // 將最後一個值追加到return_value        if (p1 <= endp)            add_next_index_stringl(return_value, p1, endp-p1, 1);    }

實現時調用了add_next_index_stringl將得到的每個字串添加到數組return_value中。add_next_index_string是此功能的核心函數。

ZEND_API int add_next_index_stringl(zval *arg, const char *str, uint length, int duplicate) {    zval *tmp;    MAKE_STD_ZVAL(tmp);    ZVAL_STRINGL(tmp, str, length, duplicate);    return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp, sizeof(zval *), NULL);}

add_next_index_stringl函數調用zend_hash_next_index_insert函數將str插入到數組中。再看看php_explode_negative_limit函數的源碼

php_explode_negative_limit

  // 如果delimiter不在string中,且limit為負數,什麼都不做,返回空的array,p2為NULL表示delimiter不在string中    if (p2 == NULL) {        /*
     如果limit <= -1,那麼什麼都不做,因此如果只有一個字串,那麼- 1 + (limit) <= 0
     返回空數組*/ } else { int allocated = EXPLODE_ALLOC_STEP, found = 0; long i, to_return; char **positions = emalloc(allocated * sizeof(char *)); // 第一個單詞的位置 positions[found++] = p1; do { if (found >= allocated) { allocated = found + EXPLODE_ALLOC_STEP;/* 保證有足夠的記憶體空間 */ positions = erealloc(positions, allocated*sizeof(char *)); } // positions儲存每個單詞的起始位置 positions[found++] = p1 = p2 + Z_STRLEN_P(delim); } while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL); // to_return 是return_value的數量,其實等於found - |limit| to_return = limit + found; /* limit至少是-1,因此不需要邊界檢查:i永遠小於found */ for (i = 0;i < to_return;i++) { /* 這個檢查是檢查to_return大於0 */ add_next_index_stringl(return_value, positions[i], (positions[i+1] - Z_STRLEN_P(delim)) - positions[i], 1 ); } efree(positions); }

php_explode_negative_limit也是跟php_implode類似的操作,找到分隔的字串之後,調用add_next_index_string函數將limit + found個字串添加到return_value數組中。

implode

string implode ( string $glue, array $pieces )

string implode ( array $pieces )

將一個一維數組的值轉換為字串

implode函數可以接收兩種參數順序。

核心代碼

if (arg2 == NULL) {        // 第二個參數為空白,第一個參數必須為數組        if (Z_TYPE_PP(arg1) != IS_ARRAY) {            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument must be an array");            return;        }        MAKE_STD_ZVAL(delim);#define _IMPL_EMPTY ""        // 預設使用""串連        ZVAL_STRINGL(delim, _IMPL_EMPTY, sizeof(_IMPL_EMPTY) - 1, 0);        SEPARATE_ZVAL(arg1);        arr = *arg1;    } else {        // 根據參數類型設定參數的值        if (Z_TYPE_PP(arg1) == IS_ARRAY) {            arr = *arg1;            convert_to_string_ex(arg2);            delim = *arg2;        } else if (Z_TYPE_PP(arg2) == IS_ARRAY) {            arr = *arg2;            convert_to_string_ex(arg1);            delim = *arg1;        } else {            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid arguments passed");            return;        }    }    // 調用php_implode函數進行轉換    php_implode(delim, arr, return_value TSRMLS_CC);

在底層實現中,implode函數處理好參數之後就調用php_implode函數進行轉換。

php_implode

  // 遍曆數組的每一個元素,判斷其類型,然後調用smart_str_appendl函數將值追加到字串中    while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **) &tmp, &pos) == SUCCESS) {        switch ((*tmp)->type) {            case IS_STRING:                smart_str_appendl(&implstr, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));                break;            case IS_LONG: {                char stmp[MAX_LENGTH_OF_LONG + 1];                str_len = slprintf(stmp, sizeof(stmp), "%ld", Z_LVAL_PP(tmp));                smart_str_appendl(&implstr, stmp, str_len);            }                break;            case IS_BOOL:                if (Z_LVAL_PP(tmp) == 1) {                    smart_str_appendl(&implstr, "1", sizeof("1")-1);                }                break;            case IS_NULL:                break;            case IS_DOUBLE: {                char *stmp;                str_len = spprintf(&stmp, 0, "%.*G", (int) EG(precision), Z_DVAL_PP(tmp));                smart_str_appendl(&implstr, stmp, str_len);                efree(stmp);            }                break;            case IS_OBJECT: {                int copy;                zval expr;                zend_make_printable_zval(*tmp, &expr, &copy);                smart_str_appendl(&implstr, Z_STRVAL(expr), Z_STRLEN(expr));                if (copy) {                    zval_dtor(&expr);                }            }                break;            default:                tmp_val = **tmp;                zval_copy_ctor(&tmp_val);                convert_to_string(&tmp_val);                smart_str_appendl(&implstr, Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));                zval_dtor(&tmp_val);                break;        }        // 添加glue字元        if (++i != numelems) {            smart_str_appendl(&implstr, Z_STRVAL_P(delim), Z_STRLEN_P(delim));        }        zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos);    }    // 在尾部添加字元0    smart_str_0(&implstr);

可以看到,php_implode函數遍曆數組的每一個元素,判斷其類型,並進行必要的類型轉換,然後調用smart_str_appendl函數將值追加到字串中。smart_str_appendl是implode實現代碼中的核心函數。

smart_str_appendl

#define smart_str_appendl(dest, src, len) \    smart_str_appendl_ex((dest), (src), (len), 0)#define smart_str_appendl_ex(dest, src, nlen, what) do {            \    register size_t __nl;                                            \    smart_str *__dest = (smart_str *) (dest);                        \                                                                    \    smart_str_alloc4(__dest, (nlen), (what), __nl);                    \    memcpy(__dest->c + __dest->len, (src), (nlen));                    \    __dest->len = __nl;                                                \} while (0)

smart_str_appendl_ex主要調用memcpy函數進行字串複製。

原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

如果本文對你有協助,點個推薦吧,謝謝^_^。

更多PHP源碼閱讀文章:

[PHP源碼閱讀]strlen函數

[PHP源碼閱讀]strpos、strstr和stripos、stristr函數

http://www.bkjia.com/PHPjc/1127592.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/1127592.htmlTechArticle[PHP源碼閱讀]explode和implode函數,explodeimplode explode和implode函數主要用作字串-數組的操作,比如擷取一段參數後根據某個字元分割字串,...

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.