簡體   English   中英

使用 str[index] 直接訪問 PHP 字符串 vs 拆分為數組

[英]PHP string direct access using str[index] vs splitting into an array

我在 PHP 中遍歷字符串中的每個字符。 目前我正在使用直接訪問

 $len=strlen($str);
 $i=0;
 while($i++<$len){
    $char=$str[$i];
    ....
 }

這讓我開始思考什么可能純粹是學術性的。 直接訪問是如何在幕后工作的,是否有一段字符串可以通過將所述字符串拆分為數組並使用數組的內部指針將索引位置保留在內存中,從而在字符循環中看到優化(盡管可能是微小的)?

TLDNR:訪問 500 萬個項目數組的每個成員會比直接訪問 500 萬個字符串的每個字符更快嗎?

您的問題的答案是您當前的方法很可能是最快的方法。

為什么?

由於 php 中的字符串只是一個字節數組,其中一個字節代表每個字符(使用 UTF-8 時),因此理論上不應該有更快的數組形式。

此外,將原始字符串的字符復制到其中的數組的任何其他實現都會增加開銷並減慢速度。

如果您的字符串的內容非常有限(例如,只允許 16 個字符而不是 256 個字符),則可能有更快的實現,但這似乎是一種邊緣情況。

訪問字符串的字節要快一個數量級。 為什么? PHP 可能只是將每個數組索引引用到它在內存中存儲每個字節的索引。 所以它可能會直接到達它需要的位置,讀入一個字節的數據,然后就完成了。 請注意,除非字符是單字節的,否則您實際上不會通過字符串字節數組訪問獲得可用字符。

當訪問一個潛在的多字節字符串(通過 mb_substr)時,需要采取一些額外的步驟來確保字符不超過一個字節,它是多少字節,然后訪問每個需要的字節並返回單獨的 [可能是多字節]字符(注意有一些額外的步驟)。

因此,我整理了一個簡單的測試代碼,只是為了表明數組字節訪問速度要快幾個數量級(但如果多字節字符作為給定字符串的字節索引存在,則不會為您提供可用字符)。 我從這里獲取了隨機字符函數( Optimal function to create a random UTF-8 string in PHP? (letter characters only) ),然后添加以下內容:

$str = rand_str( 5000000, 5000000 );
$bStr = unpack('C*', $str);

$len = count($bStr)-1;

$i = 0;
$startTime = microtime(true);
while($i++<$len) {
    $char = $str[$i];
}
$endTime = microtime(true);

echo '<pre>Array access: ' . $len . ' items: ', $endTime-$startTime, ' seconds</pre>';


$i = 0;
$len = mb_strlen($str)-1;
$startTime = microtime(true);
while($i++<$len) {
    $char = mb_substr($str, $i, 1);
    if( $i >= 100000 ) {
        break;
    }
}
$endTime = microtime(true);

echo '<pre>Substring access: ' . ($len+1) . ' (limited to ' . $i . ') items: ', $endTime-$startTime, ' seconds</pre>';

您會注意到我將 mb_substr 循環限制為 100,000 個字符。 為什么? 遍歷所有 5,000,000 個字符需要很長時間!

我的結果如何?

數組訪問:12670380 項:0.4850001335144 秒

子串訪問:5000000(限制為100000)項:17.00200009346 秒

請注意,字符串數組訪問能夠過濾所有 12,670,380 個字節——是的,來自 500 萬個字符 [許多是多字節] 中的 1260 萬個字節——只需 1/2 秒,而 mb_substring,限制為 100,000 個字符,需要 17秒!

快速回答(對於非多字節字符串,這可能是 OP 所要求的,並且對其他人也有用):直接訪問仍然更快(大約 2 倍)。 這是基於接受的答案的代碼,但對使用substr()而不是mb_substr()進行了蘋果與蘋果的比較

 $str = base64_encode(random_bytes(4000000));
 $len = strlen($str)-1;
 $i = 0;
 $startTime = microtime(true);
 while($i++<$len) {
     $char = $str[$i];
 }
 $endTime = microtime(true);

 echo '<pre>Array access: ' . $len . ' items: ', $endTime-$startTime, ' seconds</pre>';
 
 $i = 0;
 $len = strlen($str)-1;
 $startTime = microtime(true);
 while($i++<$len) {
     $char = substr($str, $i, 1);
 }
 $endTime = microtime(true);

 echo '<pre>Substring access: ' . ($len) . ' items: ', $endTime-$startTime, ' seconds</pre>';  

注意:使用隨機數的 base64 編碼來創建隨機字符串,因為 rand_str 不是定義的函數。 也許不是最隨機的,但肯定足夠隨機進行測試。

我的結果:

數組訪問:5333335 項:0.40552091598511 秒

子串訪問:5333335 項:0.87574410438538 秒

注意:也嘗試過$chars = preg_split('//', $str, -1, PREG_SPLIT_NO_EMPTY); 並遍歷$chars 這不僅變慢了,而且用 5,000,000 個字符串耗盡了空間

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM