配列を特定の文字または文字列に基づいて配列に分割する必要がある場合、explode は非常に便利ですが、~explode の仕組みをご存知ですか~~
まず第一に、explode はスペースの割り当ても行うことができるのは確かです。疑い。
コードをコピー コードは次のとおりです:
//ファイル 1: ext/standard/string.c
//まずexplodeのソースコードを見てみましょう
PHP_FUNCTION(explode)
{
char *str, *delim;
int str_len = 0, delim_len = 0; limit = LONG_MAX; /* 制限なし */
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, "空の区切り文字"); /ここで配列が開かれ、分割されたデータを保存するために使用されます
array_init(return_value);
//このため、explore('|', '') を使用します。
if ( str_len == 0) {
if (limit >= 0) {
add_next_index_stringl(return_value, "", sizeof("") - 1, 1);
return; >}
//次の 2 つは、元の文字列と区切り文字を _zval_struct 構造体に構築します。
//ZVAL_STRINGL はスペースを割り当てます~~ソース コードは後で掲載します
ZVAL_STRINGL(&zstr, str, str_len, 0 ; {
php_explode(&zdelim, &zstr, return_value, 制限);
} else if (limit php_explode_negative_limit(&zdelim, &zstr, return_value, 制限);
add_index_stringl(return_value, 0, str, str_len, 1);
}
}
コードをコピー
コードは次のとおりです。
//ZVAL_STRINGL のソース コード:
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.c
ZEND_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
}
memcpy(p, s, length); //スペースを割り当てます
p[length] = 0; >//また、substr および strrchr で使用される ZVAL_STRING strstr も、アピールの実装を使用します
以下は、explode の 3 番目のパラメーター制限に基づく呼び出しの分析です。条件は、爆発の最後の 3 行、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 内のセパレータの位置。このメソッドは、
p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp) を見つけるために strrpos と strpos でも使用されていることがわかります。 ;
if (p2 == NULL) {
//このため、explode('|', 'abc') を呼び出すと、結果は array(0 =>) になります。 'abc')
add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1);
} else {
//最後までループして次のセパレータの位置を取得します
{
// 部分文字列 (前の位置とこの位置の間のセクション、初めて前の位置が先頭である場合) を取得します
add_next_index_stringl(return_value, p1, p2 - p1, 1);
/ /区切り文字の位置 p2 に配置 区切り文字の長さの位置
//たとえば、区切り文字 ='|'、元の文字列 = 'ab|c'、p2 = 2、その後、p1=2 1=3
p1 = p2 Z_STRLEN_P (delim);
} while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL &&
--limit > 1);最後の区切り文字の後の文字列を結果に挿入します array
//explode('|', 'avc|sdf'); array(0 => 'avc', 1= > 'sdf');
if (p1 add_next_index_stringl(return_value, p1, endp-p1, 1);
}
}
2. 制限 php_explode_negative_limit メソッドを呼び出します
コードは次のとおりです: PHPAPI void php_explode_negative_limit(zval * delim, zval *str, zval *return_value, 長い制限)
{#define EXPLODE_ALLOC_STEP 64
char *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_STRLEN_P(delim), endp); //ここでは処理されないため、explore('|', 'abc', -1) は不正となり、値を取得できません
/*
limit 何もしないと空の配列が返されます
*/
} else {
int assign = EXPLODE_ALLOC_STEP, found = 0
long i, to_return;
char **positions = emalloc(allocated * sizeof(char *));
//この配列は、すべての部分文字列の読み取りを保存するために使用されます。 >positions[found] = p1; //もちろん、開始位置を保存する必要があります
//次の 2 つのループ、最初のループは文字列内に出現するすべての区切り文字の位置をループし、次のループを保存します。 1 つの部分文字列読み取り位置
do {
if (found >=割り当て済み) {
allocated = found EXPLODE_ALLOC_STEP;/* 十分なメモリがあることを確認してください */
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);
//これは、返された結果が配列から読み取られる部分文字列です。
/* 制限は少なくとも -1 であるため、値はありません。境界チェックの必要性: i は常に見つかった値より小さくなります */
for (i = 0;i < to_return;i ) { /* これは to_return > 0 についてもチェックします */
add_next_index_stringl(return_value,位置[i],
(位置[i 1] - Z_STRLEN_P(delim)) - 位置[i],
1
); // 非常に重要、メモリを解放します
}
#undef EXPLODE_ALLOC_STEP
}
3. 制限 = 1 または制限 = 0:
最初の条件がすべて満たされたとき、および 2 番目の条件が次の場合満たされていない場合は、このブランチに入ります。このブランチは、単純にソース文字列を出力配列、explode('|', 'avc|sd', 1) またはexplode('|' , 'avc|sd', 0)に入れるだけです。 array(0 => 'avc|sd');
コードをコピー
コードは次のとおりです:
ZEND_API int add_next_index_stringl(zval *arg, const char *str, uint length, int Duplicate) /* { {{ */ { zval *tmp; ZVAL_STRINGL(tmp, str, length, unique) return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp, sizeof(zval *); );
}//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(h t , 0, pData 、nDataSize、 pDest, HASH_NEXT_INSERT ZEND_FILE_LINE_CC)
//zend/zend_hash.c
///長すぎます~~~~もう投稿しません
可視 (割り当てられたスペースを除く)、
limit>1 の場合、効率は O(N) [N は制限値]、
limitlimit=1 または limit=0 の場合、効率は O(1)