使用str [index] vs拆分成数组的PHP字符串直接访问

时间:2016-07-12 19:54:44

标签: php arrays string

我在PHP中迭代字符串中的每个字符。 目前我正在使用直接访问

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

这让我思考什么可能纯粹是学术性的。 直接访问是如何工作的,并且是否有一个字符串的长度,通过将所述字符串拆分为数组并使用数组的内部指针将索引位置保留在内存中,可以在字符循环中看到优化(尽管可能是微观)?

TLDNR: 访问500万个项目数组的每个成员会比直接访问500万个字符串的每个字符更快吗?

3 个答案:

答案 0 :(得分:1)

你的问题的答案是你当前的方法很可能是最快的方法。

为什么?

因为php中的字符串只是一个字节数组,其中一个字节代表每个字符(当使用UTF-8时),理论上不应该有更快的数组形式。

此外,您复制原始字符串字符的数组的任何其他实现都会增加开销并减慢速度。

如果你的字符串内容非常有限(例如,只允许16个字符而不是256个字符),那么可能会有更快的实现,但这似乎是一个边缘情况。

答案 1 :(得分:1)

访问字符串的字节速度快一个数量级。为什么? 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万字符的12.6百万字节[很多是多字节] - 只需1/2秒,而mb_substring,限制为100,000个字符,花了17秒!

答案 2 :(得分:0)

快速回答(对于非多字节字符串,可能是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>';  

注意:由于rand_str不是定义的函数,所以使用base64编码的随机数来创建随机字符串。也许不是最随机的,但对于测试来说绝对足够随机。

我的结果:

数组访问:5333335项:0.40552091598511秒

子字符串访问:5333335项:0.87574410438538秒

注意:还尝试做$chars = preg_split('//', $str, -1, PREG_SPLIT_NO_EMPTY);并遍历$chars。这样不仅速度较慢,而且还用了5,000,000个字符串来耗尽空间