目次
[PHP源码阅读]explode和implode函数,explodeimplode
explode
$limit
$delimiter
核心源码
php_explode
php_explode_negative_limit
implode
核心代码
php_implode
smart_str_appendl
 
ホームページ php教程 php手册 [PHP源码阅读]explode和implode函数,explodeimplode

[PHP源码阅读]explode和implode函数,explodeimplode

Jun 13, 2016 am 08:39 AM
explode

[PHP源码阅读]explode和implode函数,explodeimplode

explode和implode函数主要用作字符串-数组的操作,比如获取一段参数后根据某个字符分割字符串,或者将一个数组的结果使用一个字符合并成一个字符串输出。在PHP中经常会用到这两个函数,因此有必要了解一下其原理。

explode

<p>array explode ( string $delimiter, string $string, [ , $limit ] )</p>
ログイン後にコピー

返回由字符串组成的数组,每个元素都是string的一个子串,被字符串$delimiter作为边界点分割出来。

$limit

如果设置了$limit,且为正数,则返回的数组最多包含$limit个元素,最后的那个元素将包含$string的剩余部分。

如果$limit是负数,则返回除了最后的-$limit个元素外的所有元素。

如果$limit是0,则会被当做1。

$delimiter

如果$delimiter为空,则函数返回FALSE。如果delimiter不在string中,且$limit为负数,则返回空数组。

核心源码

    <span>//</span><span> 如果delimiter为空字符串,返回FALSE</span>
    <span>if</span> (delim_len == <span>0</span><span>) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, </span><span>"</span><span>Empty delimiter</span><span>"</span><span>);
        RETURN_FALSE;
    }

    </span><span>//</span><span> 初始化返回的数组</span>
<span>    array_init(return_value);

    </span><span>if</span> (str_len == <span>0</span><span>) {
          </span><span>if</span> (limit >= <span>0</span><span>) {
              </span><span>//</span><span> 如果字符串为空且limit大于等于0,则返回一个包含空字符串的数组,注意此处sizeof("") == 1</span>
            add_next_index_stringl(return_value, <span>""</span>, <span>sizeof</span>(<span>""</span>) - <span>1</span>, <span>1</span><span>);
        }
        </span><span>return</span><span>;
    }

    </span><span>//</span><span> 初始化zstr和zdelim的字符串变量</span>
    ZVAL_STRINGL(&zstr, str, str_len, <span>0</span><span>);
    ZVAL_STRINGL(</span>&zdelim, delim, delim_len, <span>0</span><span>);
    </span><span>if</span> (limit > <span>1</span><span>) {
        </span><span>//</span><span> limit大于1,limit默认是LONG_MAX</span>
        php_explode(&zdelim, &<span>zstr, return_value, limit);
    } </span><span>else</span> <span>if</span> (limit < <span>0</span><span>) {
        </span><span>//</span><span> limit 为负数</span>
        php_explode_negative_limit(&zdelim, &<span>zstr, return_value, limit);
    } </span><span>else</span><span> {
        </span><span>//</span><span> limit为0,被当作1处理,返回整个字符串,add_index_stringl函数将str追加到数组return_value中</span>
        add_index_stringl(return_value, <span>0</span>, str, str_len, <span>1</span><span>);
    }</span>
ログイン後にコピー

处理完特殊情况和初始化变量后,就调用php_explode/php_explode_negative_limit函数进行下一步处理。下面是php_explode函数的源码

php_explode

<span>  endp </span>= Z_STRVAL_P(str) +<span> Z_STRLEN_P(str);

    </span><span>//</span><span> p1指向字符串的开始</span>
    p1 =<span> Z_STRVAL_P(str);
    </span><span>//</span><span> p2指向第一个分隔符的位置 ,找出分隔符位置主要用的是php_memnstr函数</span>
    p2 =<span> php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);

    </span><span>if</span> (p2 ==<span> NULL) {
        </span><span>//</span><span> p2为NULL表示找不到分隔符,直接返回整个字符串</span>
        add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), <span>1</span><span>);
    } </span><span>else</span><span> {
        </span><span>do</span><span> {
            </span><span>//</span><span> 将p1添加到return_value数组中 ,移动到下一个分隔符的位置</span>
            add_next_index_stringl(return_value, p1, p2 - p1, <span>1</span><span>);
            p1 </span>= p2 +<span> Z_STRLEN_P(delim);
        } </span><span>while</span> ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL &&
                 --limit > <span>1</span><span>);

        </span><span>//</span><span> 将最后一个值追加到return_value</span>
        <span>if</span> (p1 <=<span> endp)
            add_next_index_stringl(return_value, p1, endp</span>-p1, <span>1</span><span>);
    }</span>
ログイン後にコピー

实现时调用了add_next_index_stringl将得到的每个字符串添加到数组return_value中。add_next_index_string是此功能的核心函数。

ZEND_API <span>int</span> add_next_index_stringl(zval *arg, <span>const</span> <span>char</span> *str, <span>uint</span> length, <span>int</span><span> duplicate) 
{
    zval </span>*<span>tmp;

    MAKE_STD_ZVAL(tmp);
    ZVAL_STRINGL(tmp, str, length, duplicate);

    </span><span>return</span> zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp, <span>sizeof</span>(zval *<span>), NULL);
}</span>
ログイン後にコピー

add_next_index_stringl函数调用zend_hash_next_index_insert函数将str插入到数组中。再看看php_explode_negative_limit函数的源码

php_explode_negative_limit

<span>  //</span><span> 如果delimiter不在string中,且limit为负数,什么都不做,返回空的array,p2为NULL表示delimiter不在string中</span>
    <span>if</span> (p2 ==<span> NULL) {
        </span><span>/*<br />     如果limit <= -1,那么什么都不做,因此如果只有一个字符串,那么- 1 + (limit) <= 0<br />     返回空数组</span><span>*/</span><span>
    } </span><span>else</span><span> {
        </span><span>int</span> allocated = EXPLODE_ALLOC_STEP, found = <span>0</span><span>;
        </span><span>long</span><span> i, to_return;
        </span><span>char</span> **positions = emalloc(allocated * <span>sizeof</span>(<span>char</span> *<span>));
        </span><span>//</span><span> 第一个单词的位置</span>
        positions[found++] =<span> p1;
        </span><span>do</span><span> {
            </span><span>if</span> (found >=<span> allocated) {
                allocated </span>= found + EXPLODE_ALLOC_STEP;<span>/*</span><span> 保证有足够的内存空间 </span><span>*/</span><span>
                positions </span>= erealloc(positions, allocated*<span>sizeof</span>(<span>char</span> *<span>));
            }
            </span><span>//</span><span> positions保存每个单词的起始位置</span>
            positions[found++] = p1 = p2 +<span> Z_STRLEN_P(delim);
        } </span><span>while</span> ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) !=<span> NULL);

        </span><span>//</span><span> to_return 是return_value的数量,其实等于found - |limit|</span>
        to_return = limit +<span> found;
        </span><span>/*</span><span> limit至少是-1,因此不需要边界检查:i永远小于found </span><span>*/</span>
        <span>for</span> (i = <span>0</span>;i < to_return;i++) { <span>/*</span><span> 这个检查是检查to_return大于0 </span><span>*/</span><span>
            add_next_index_stringl(return_value, positions[i],
                    (positions[i</span>+<span>1</span>] - Z_STRLEN_P(delim)) -<span> positions[i],
                    </span><span>1</span><span>
                );
        }
        efree(positions);
    }</span>
ログイン後にコピー

php_explode_negative_limit也是跟php_implode类似的操作,找到分隔的字符串之后,调用add_next_index_string函数将limit + found个字符串添加到return_value数组中。

implode

<p>string implode ( string $glue, array $pieces )</p>
<p>string implode ( array $pieces )</p>
ログイン後にコピー

将一个一维数组的值转换为字符串

implode函数可以接收两种参数顺序。

核心代码

<span>if</span> (arg2 ==<span> NULL) {
        </span><span>//</span><span> 第二个参数为空,第一个参数必须为数组</span>
        <span>if</span> (Z_TYPE_PP(arg1) !=<span> IS_ARRAY) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, </span><span>"</span><span>Argument must be an array</span><span>"</span><span>);
            </span><span>return</span><span>;
        }

        MAKE_STD_ZVAL(delim);
</span><span>#define</span> _IMPL_EMPTY ""
        <span>//</span><span> 默认使用""连接</span>
        ZVAL_STRINGL(delim, _IMPL_EMPTY, <span>sizeof</span>(_IMPL_EMPTY) - <span>1</span>, <span>0</span><span>);

        SEPARATE_ZVAL(arg1);
        arr </span>= *<span>arg1;
    } </span><span>else</span><span> {
        </span><span>//</span><span> 根据参数类型设置参数的值</span>
        <span>if</span> (Z_TYPE_PP(arg1) ==<span> IS_ARRAY) {
            arr </span>= *<span>arg1;
            convert_to_string_ex(arg2);
            delim </span>= *<span>arg2;
        } </span><span>else</span> <span>if</span> (Z_TYPE_PP(arg2) ==<span> IS_ARRAY) {
            arr </span>= *<span>arg2;
            convert_to_string_ex(arg1);
            delim </span>= *<span>arg1;
        } </span><span>else</span><span> {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, </span><span>"</span><span>Invalid arguments passed</span><span>"</span><span>);
            </span><span>return</span><span>;
        }
    }

    </span><span>//</span><span> 调用php_implode函数进行转换</span>
    php_implode(delim, arr, return_value TSRMLS_CC);
ログイン後にコピー

在底层实现中,implode函数处理好参数之后就调用php_implode函数进行转换。

php_implode

<span>  //</span><span> 遍历数组的每一个元素,判断其类型,然后调用smart_str_appendl函数将值追加到字符串中</span>
    <span>while</span> (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (<span>void</span> **) &tmp, &pos) ==<span> SUCCESS) {
        </span><span>switch</span> ((*tmp)-><span>type) {
            </span><span>case</span><span> IS_STRING:
                smart_str_appendl(</span>&<span>implstr, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
                </span><span>break</span><span>;

            </span><span>case</span><span> IS_LONG: {
                </span><span>char</span> stmp[MAX_LENGTH_OF_LONG + <span>1</span><span>];
                str_len </span>= slprintf(stmp, <span>sizeof</span>(stmp), <span>"</span><span>%ld</span><span>"</span><span>, Z_LVAL_PP(tmp));
                smart_str_appendl(</span>&<span>implstr, stmp, str_len);
            }
                </span><span>break</span><span>;

            </span><span>case</span><span> IS_BOOL:
                </span><span>if</span> (Z_LVAL_PP(tmp) == <span>1</span><span>) {
                    smart_str_appendl(</span>&implstr, <span>"</span><span>1</span><span>"</span>, <span>sizeof</span>(<span>"</span><span>1</span><span>"</span>)-<span>1</span><span>);
                }
                </span><span>break</span><span>;

            </span><span>case</span><span> IS_NULL:
                </span><span>break</span><span>;

            </span><span>case</span><span> IS_DOUBLE: {
                </span><span>char</span> *<span>stmp;
                str_len </span>= spprintf(&stmp, <span>0</span>, <span>"</span><span>%.*G</span><span>"</span>, (<span>int</span><span>) EG(precision), Z_DVAL_PP(tmp));
                smart_str_appendl(</span>&<span>implstr, stmp, str_len);
                efree(stmp);
            }
                </span><span>break</span><span>;

            </span><span>case</span><span> IS_OBJECT: {
                </span><span>int</span><span> copy;
                zval expr;
                zend_make_printable_zval(</span>*tmp, &expr, &<span>copy);
                smart_str_appendl(</span>&<span>implstr, Z_STRVAL(expr), Z_STRLEN(expr));
                </span><span>if</span><span> (copy) {
                    zval_dtor(</span>&<span>expr);
                }
            }
                </span><span>break</span><span>;

            </span><span>default</span><span>:
                tmp_val </span>= **<span>tmp;
                zval_copy_ctor(</span>&<span>tmp_val);
                convert_to_string(</span>&<span>tmp_val);
                smart_str_appendl(</span>&<span>implstr, Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
                zval_dtor(</span>&<span>tmp_val);
                </span><span>break</span><span>;

        }

        </span><span>//</span><span> 添加glue字符</span>
        <span>if</span> (++i !=<span> numelems) {
            smart_str_appendl(</span>&<span>implstr, Z_STRVAL_P(delim), Z_STRLEN_P(delim));
        }
        zend_hash_move_forward_ex(Z_ARRVAL_P(arr), </span>&<span>pos);
    }
    </span><span>//</span><span> 在尾部添加字符0</span>
    smart_str_0(&implstr);
ログイン後にコピー

可以看到,php_implode函数遍历数组的每一个元素,判断其类型,并进行必要的类型转换,然后调用smart_str_appendl函数将值追加到字符串中。smart_str_appendl是implode实现代码中的核心函数。

smart_str_appendl

<span>#define</span> smart_str_appendl(dest, src, len) \<span>
    smart_str_appendl_ex((dest), (src), (len), </span><span>0</span><span>)

</span><span>#define</span> smart_str_appendl_ex(dest, src, nlen, what) do {            \<span>
    register size_t __nl;                                            \
    smart_str </span>*__dest = (smart_str *<span>) (dest);                        \
                                                                    \
    smart_str_alloc4(__dest, (nlen), (what), __nl);                    \
    memcpy(__dest</span>->c + __dest-><span>len, (src), (nlen));                    \
    __dest</span>->len =<span> __nl;                                                \
} </span><span>while</span> (<span>0</span>)
ログイン後にコピー

smart_str_appendl_ex主要调用memcpy函数进行字符串复制。

 

原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

如果本文对你有帮助,点个推荐吧,谢谢^_^。

 

 

更多PHP源码阅读文章:

[PHP源码阅读]strlen函数

[PHP源码阅读]strpos、strstr和stripos、stristr函数

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHPのexplode関数の使い方とエラー解決方法 PHPのexplode関数の使い方とエラー解決方法 Mar 10, 2024 am 09:18 AM

PHP のexplode関数は、文字列を配列に分割するために使用される関数で、非常に一般的で柔軟性があります。分解関数を使用する過程で、エラーや問題が発生することがよくありますが、この記事では、分解関数の基本的な使用方法を紹介し、エラー報告を解決するいくつかの方法を紹介します。 1.explode関数の基本的な使い方 PHPでは、explode関数の基本的な構文は次のとおりです。explode(string$separator,string$stri

PHP で展開関数を使用する場合の一般的なエラーと解決策 PHP で展開関数を使用する場合の一般的なエラーと解決策 Mar 11, 2024 am 08:33 AM

タイトル: PHP でexplode関数を使用する際の一般的なエラーと解決策 PHPでは、explode関数は、文字列を配列に分割するために使用される一般的な関数です。ただし、不適切な使用や不正なデータ形式が原因で、いくつかの一般的なエラーが発生する可能性があります。この記事では、explode 関数の使用時に発生する可能性のある問題を分析し、解決策と具体的なコード例を示します。間違い 1: delimiter パラメーターが渡されない。explode 関数を使用するときに最もよくある間違いの 1 つは、delimiter パラメーターが渡されないことです。

explode関数とimplode関数を使用して文字列を分割および結合する explode関数とimplode関数を使用して文字列を分割および結合する Jun 15, 2023 pm 08:42 PM

PHP プログラミングでは、文字列の処理は頻繁に必要な操作です。その中で、文字列の分割と結合は 2 つの一般的な要件です。これらの操作をより便利に実行するために、PHP は 2 つの非常に実用的な関数、つまり、explode 関数と implode 関数を提供します。この記事では、これら2つの機能の使い方と実践スキルを紹介します。 1.explode関数explode関数は、指定された区切り文字に従って文字列を分割し、配列を返すために使用されます。その関数プロトタイプは次のとおりです。

PHP関数「explode」を使用して文字列を配列に分割する PHP関数「explode」を使用して文字列を配列に分割する Jul 24, 2023 pm 11:09 PM

PHP 関数「explode」を使用して、文字列を配列に分割します。PHP 開発では、指定された区切り文字に従って文字列を分割する必要がある状況によく遭遇します。このとき、PHP の組み込み関数「explode」を使用して文字列を配列に変換できます。この記事では、「explode」関数を使用して文字列を分割する方法と、関連するコード例を紹介します。 「explode」関数の基本構文は次のとおりです。

php 爆発でエラーが報告された場合はどうすればよいですか? php 爆発でエラーが報告された場合はどうすればよいですか? Jan 18, 2023 am 10:13 AM

PHP 爆発エラーの解決策: 1. 間違った PHP コードを見つけて開きます; 2. 爆発関数部分を見つけます; 3. コードを「dump(explode(',',$str));die;」に変更します。 、配列をカンマで区切るだけを使用します。

PHP 関数「explode」を使用して文字列を複数の部分文字列に分割する PHP 関数「explode」を使用して文字列を複数の部分文字列に分割する Jul 25, 2023 pm 06:29 PM

PHP 関数「explode」を使用して文字列を複数の部分文字列に分割する PHP プログラミングでは、文字列を複数の部分文字列に分割する必要がある状況によく遭遇します。このとき、PHP には、この関数を簡単に実装できる非常に便利な関数「explode」が用意されています。 「explode」関数の構文は次のとおりです。 arrayexplode(string$delimiter,string$string[,in

PHPでexplode関数を使用して文字列を分割する方法 PHPでexplode関数を使用して文字列を分割する方法 Jun 26, 2023 pm 12:03 PM

PHP 言語には、文字列を迅速かつ効率的に処理するのに役立つ基本的な関数が多数あります。その中でもexplode関数は非常に実用的な文字列分割関数です。指定された区切り文字に従って文字列を配列に分割し、より柔軟な文字列操作を実行できます。この記事では、PHPで文字列を分割するexplode関数の使い方を紹介します。 1.explode関数の形式 PHP言語でのexplode関数の形式は次のとおりです。

PHPのexplode()関数とは何ですか? PHPのexplode()関数とは何ですか? Sep 04, 2023 pm 01:01 PM

この記事では、事前定義された組み込み PHP 関数である PHPExplode() 関数の使用方法を学びます。 explode関数は、「文字列を小さな内容に分割する」ために使用します。PHPのexplode関数を使用すると、文字列をブレークすることで小さな内容に分割できます。この区切り文字をデリミタと呼びます。 構文explode(delimiter, string, Number of elements) パラメータExplosion 関数は 3 つのパラメータを受け入れます, そのうち 2 つは必須で、1 つはオプションです. これら 3 つのパラメータについて説明しましょう. セパレータ この文字は文字列が分割される臨界点または点を指定します。つまり、この文字が文字列内で見つかるたびに、これは、配列内の 1 つの要素の終わりと別の要素の始まりを象徴します。

See all articles