從源碼去理解PHP的explode()函數

來源:互聯網
上載者:User

標籤:des   http   os   io   使用   ar   for   檔案   資料   

當我們需要將一個數組根據某個字元或字串進行分割成數組的時候,explode()函數很好用,但是你知道explode()是怎麼工作的嗎?截取字串的問題,都會避免不了重新分配空間的消耗,explode也是會分配空間的,毫無疑問。老品牌娛樂城

//檔案1:ext/standard/string.c//先來看下explode的原始碼PHP_FUNCTION(explode){char *str, *delim;int str_len = 0, delim_len = 0;long limit = LONG_MAX; /* No limit */zval zdelim, zstr;   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &delim, &delim_len, &str, &str_len, &limit) == FAILURE) {return;}if (delim_len == 0) {php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");RETURN_FALSE;}  //這裡會開闢一個數組,用來存放分割後的資料array_init(return_value);//因為這個,我們用explode(‘|‘, ‘‘);成為了合法的if (str_len == 0) {if (limit >= 0) {add_next_index_stringl(return_value, "", sizeof("") - 1, 1);} return;}//下面這兩個是將原字串和分割符都構建成_zval_struct 結構,//ZVAL_STRINGL會分配空間哦~~原始碼隨後貼出ZVAL_STRINGL(&zstr, str, str_len, 0);   ZVAL_STRINGL(&zdelim, delim, delim_len, 0);//limit值是explode中允許傳遞的explode的第三個參數,它允許正負if (limit > 1) {php_explode(&zdelim, &zstr, return_value, limit);} else if (limit < 0) {php_explode_negative_limit(&zdelim, &zstr, return_value, limit);} else {add_index_stringl(return_value, 0, str, str_len, 1);}}

再來看一段:

//ZVAL_STRINGL的原始碼:  //檔案2:zend/zend_API.c    #define ZVAL_STRINGL(z, s, l, duplicate) {    const char *__s=(s); int __l=l;        Z_STRLEN_P(z) = __l;                Z_STRVAL_P(z) = (duplicate?estrndup(__s, __l):(char*)__s);Z_TYPE_P(z) = IS_STRING;            }....//estrndup才是主菜://檔案3:zend/zend_alloc.h#define estrndup(s, length)    _estrndup((s), (length) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)....//_estrndup的實現: zend/zend_alloc.cZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC){char *p;p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);if (UNEXPECTED(p == NULL)) {return p;}memcpy(p, s, length);   //分配空間p[length] = 0;return p;}//另外在substr和strrchr strstr中用到的ZVAL_STRING也是使用了上訴的實現

下面根據explode的第三個參數limit來分析調用:條件對應的是explode中最後的三行,對limit條件的不同。註: limit在預設的時候(沒有傳遞),他的預設值是LONG_MAX,也就是屬於分支1的情況。

1、limit > 1 :

調用php_explode方法,該方法也可以在ext/standard/string.c中找到,並且是緊接著explode實現的上面出現(所以在尋找本函數中調用來自本檔案的方法的時候很方便,幾乎無一列外都是在該函數的緊接著的上面^_^)。

PHPAPI void php_explode(zval *delim, zval *str, zval *return_value, long limit) {char *p1, *p2, *endp;//先得到的是源字串的末尾位置的指標endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);//記錄開始位置p1 = Z_STRVAL_P(str);//下面這個是獲得分割符在str中的位置,可以看到在strrpos和strpos中也用到了這個方法去定位p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);if (p2 == NULL) {//因為這個,所以當我們調用explode(‘|‘, ‘abc‘);是合法的,出來的的就是array(0 => ‘abc‘)add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1);} else {//依次迴圈獲得下一個分隔字元的位置,直到結束do {//將得到的子字串(上個位置到這個位置中間的一段,第一次的時候上個位置就是開始add_next_index_stringl(return_value, p1, p2 - p1, 1);//定位到分隔字元位置p2+分隔字元的長度的位置//比如,分隔字元=‘|‘, 原字串= ’ab|c‘, p2 = 2,  則p1=2+1=3p1 = p2 + Z_STRLEN_P(delim);} while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL &&                 --limit > 1);//將最後的一個分隔字元後面的字串放到結果數組中//explode(‘|‘, ‘avc|sdf‘);   => array(0 => ‘avc‘, 1= > ‘sdf‘)if (p1 <= endp)add_next_index_stringl(return_value, p1, endp-p1, 1);}}
2、limit < 0 :

調用php_explode_negative_limit方法:

PHPAPI void php_explode_negative_limit(zval *delim, zval *str, zval *return_value, long limit) {#define EXPLODE_ALLOC_STEP 64char *p1, *p2, *endp;endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);p1 = Z_STRVAL_P(str);p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);if (p2 == NULL) {//它這裡竟然沒有處理,那explode(‘|‘, ‘abc‘, -1) 就成非法的了,獲得不了任何值/*do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0by doing nothing we return empty array*/} else {int allocated = EXPLODE_ALLOC_STEP, found = 0;        long i, to_return;char **positions = emalloc(allocated * sizeof(char *));//注意這裡的positions的聲明,這個數組是用來儲存所有子字串的讀取位置positions[found++] = p1;   //當然起始位置還是需要儲存//下面兩個迴圈,第一個是迴圈所有在字串中出現的分隔字元位置,並儲存下一個子字串讀取位置起來do {if (found >= allocated) {allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */positions = erealloc(positions, allocated*sizeof(char *));}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 = limit + found;/* limit is at least -1 therefore no need of bounds checking : i will be always less than found */for (i = 0;i < to_return;i++) { /* this checks also for to_return > 0 */add_next_index_stringl(return_value, positions[i], (positions[i+1] - Z_STRLEN_P(delim)) - positions[i],1);}efree(positions);//很重要,釋放記憶體}#undef EXPLODE_ALLOC_STEP}
3、limit = 1 or limit = 0 :

當所有第一和第二條件都不滿足的時候,就進入的這個分支,這個分支很簡單就是將源字串放到輸出數組中,explode(‘|‘, ‘avc|sd‘, 1) or explode(‘|‘, ‘avc|sd‘, 0)  都將返回array(0 => ‘avc|sd‘);

//add_index_stringl原始碼//檔案4:zend/zend_API.cZEND_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);}//zend_hash_next_index_insert//zend/zend_hash.h#define zend_hash_next_index_insert(ht, pData, nDataSize, pDest)          _zend_hash_index_update_or_next_insert(ht, 0, pData, nDataSize, pDest, HASH_NEXT_INSERT ZEND_FILE_LINE_CC)//zend/zend_hash.c///太長了~~~~不貼了

可見(不包含分配空間這些),當limit>1的時候,效率是O(N)【N為limit值】,當limit < 0的時候,效率是O(N+M)【N為limit值, M 為分割符出現次數】,當limit=1 or  limit=0 的時候, 效率是O(1)。

從源碼去理解PHP的explode()函數

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.