0

这是一个愚蠢的例子,但确切地说明了我的问题。在某些情况下,数组已成功修改,而在另一些情况下则没有,为什么?值是按值赋予foreach的吗?并且输出也搞砸了,有些行似乎有 '\r\n' 其他行没有。

$arr = file('text.txt');
echo '<pre>';

foreach( $arr as $x => $line){
    if( $x % 3){ unset( $arr[$x]); } // this works
    else{ $arr[$x+1] += 1;} // this don't
    echo "[$x] => ${arr[$x+1]}";
}


print_r( $arr);

文本.txt:

0
1
2
3
4
5
6

输出:

[0] => 2[1] => 2
[2] => 3
[3] => 5[4] => 5
[5] => 6[6] => 1Array
(
    [0] => 0

    [ 3] => 3

    [6] => 6
    [7] => 1
)

编辑:

这个例子并没有真正完成任何事情,只是为了表明发生了一些意想不到的事情是没有用的,所以,这里有一些更接近我需要做的事情:

<?php

$arr = file('text.txt');
echo '<pre>';

foreach( $arr as $x => $line){
    if( preg_match("/word$/", $line)){
        $line = preg_replace( "/word$/", '', $line);
        $arr[$x+1] = 'word ' . $arr[$x+1];
    }
}


print_r( $arr);

文本.txt:

test0
test1word
test2

数组中的预期值:

[0] => test0
[1] => test1
[2] => 字 test2

4

6 回答 6

3

在迭代数组时修改数组通常是不安全的,并且可能导致意外行为。

于 2010-07-27T15:06:29.693 回答
3

除非您通过引用传递,否则您不能更改 foreach 迭代器中的值。正如PHP 手册页所述

注意:除非数组被引用,否则 foreach 对指定数组的副本而不是数组本身进行操作。foreach 对数组指针有一些副作用。不要在 foreach 期间或之后依赖数组指针而不重置它。

因此,您需要将 foreach 行更新为:

foreach( $arr as $x => &$line){
...
于 2010-07-27T15:13:22.373 回答
0

您的第二个示例仍然是预期的行为...您正在处理数组及其值的副本,而不是实际的数组值,除非您使用“通过引用”

foreach( $arr as $x => &$line){
    if( preg_match("/word$/", $line)){
        $line = preg_replace( "/word$/", '', $line);
        $arr[$x+1] = 'word ' . $arr[$x+1];
    }
}
unset($line);

注意使用 &$line 而不是 $line,在循环结束后取消设置总是最安全的

编辑

引用PHP 手册

注意:除非数组被引用,否则 foreach 对指定数组的副本而不是数组本身进行操作。foreach 对数组指针有一些副作用。不要在 foreach 期间或之后依赖数组指针而不重置它。

编辑

我不建议在 foreach() 中使用引用,它真的很慢,在我的情况下它慢了 16 倍。添加此行的解决方案: $line = $arr[$x]; 在循环的开始,它似乎做了一些魔术,一切都如我所料

并不是真正的魔术。它只是通过键 ($x) 直接从数组中直接用 $line 覆盖通过 foreach 循环提取的 $line 的值。

YMMV,但对我来说似乎并不慢。

以下测试脚本:

$arr = range(1,9999);


$callStartTime = microtime(true);

foreach($arr as &$line) {
    $line += 1;
}
unset($line);

$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
echo '<br />Call time to access by reference was '.sprintf('%.4f',$callTime)." seconds<br />\n";


foreach($arr as $x => &$line) {
    $line += 1;
}
unset($line);

$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
echo '<br />Call time to access by reference (retrieving key as well) was '.sprintf('%.4f',$callTime)." seconds<br />\n";


$callStartTime = microtime(true);

foreach($arr as $x => $line) {
    $arr[$x] += 1;
}
unset($line);

$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
echo '<br />Call time and then access array element directly was '.sprintf('%.4f',$callTime)." seconds<br />\n";

$callStartTime = microtime(true);

foreach(array_keys($arr) as $x) {
    $arr[$x] += 1;
}

$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
echo '<br />Call time to access array_keys was '.sprintf('%.4f',$callTime)." seconds<br />\n";

返回以下时间:

Call time to access by reference was 0.0018 seconds
Call time to access by reference (retrieving key as well) was 0.0039 seconds
Call time to access key and then access array element directly was 0.0077 seconds
Call time to access array_keys was 0.0071 seconds
于 2010-07-27T16:49:27.460 回答
0

好吧,您的示例取消设置不能被 3 整除的每个元素。现在,$arr[$x+1] += 1;您将值增加 1,并且在下一次迭代中,它将被取消设置,因为它不能被 3 整除。

于 2010-07-27T15:10:01.497 回答
0

这是预期的行为,它会取消设置不是 3 的倍数的所有内容,然后尝试将未设置的变量增加 1。

于 2010-07-27T15:10:05.060 回答
0

你想用这个来达到什么目的?

if( $x % 3){ 

当 $x 为 0 时,$x % 3 为 0,故为假;当 $x 为 1 时,$x % 3 为 1,因此为真;当 $x 为 2 时,$x % 3 为 2,因此为真……所以当 $x 为 0 时,它将增加 $x+1 (1),然后立即取消设置条目 1 和 2;当 $x 为 3 时增加 $x+1(条目 4);然后取消设置条目 4 和 5 等。

你的意思是

if(( $x % 3) == 0) { 
于 2010-07-27T15:17:01.837 回答