具有传递引用的Foreach给出的结果与传递值不同

时间:2011-11-10 07:05:09

标签: php foreach pass-by-reference

我在PHP中编写了几行代码来重命名数组中的重复值(带有一些灵感from here)。 基本上,我使用原始数组($ headers []),同时使用另一个数组的键($ header_test [])来跟踪重复项。如果有重复,我在$ headers []中更改该元素的值。

但奇怪的是,我没有通过在foreach中通过引用获得正确的结果。我必须使用完整的$ array_name [$ key] = $ new_value格式来实际设置值。那是为什么?

(剧透警报:VolkerK的答案是正确的 - 需要取消设置$ header(foreach“$ value”变量),然后才能正常工作。)

下面:

使用此输入:

$headers = array('Abc 123 ghi',
            'dangdarn',
            'oops32',
            'poss dup',
            'poss dup',
            'pos  _s_ dup',
            'bad chars\'& 3% 9'
           );

然后应用我认为不会影响手头问题的自定义函数:

    function mysql_clean_string($string) {
    //remove non alnum characters
    $string =  preg_replace("/[^a-zA-Z0-9\s]/", "_", $string);

    // replace 1+ spaces or 1+ '_' with a single '_'
    $string =  preg_replace("/[ _]+/", "_", $string);

    $string = trim($string,'_');

    if(strlen($string) > 20) {
        $string = substr_replace($string,'',strpos($string,'_',20));
    }

    $string = strtolower($string);

    return $string;
}

array_walk($headers,'mysql_clean_string'); // limit headers to alnum chars

通过引用传递数组值无法正常工作(请参阅下面的var_dump()):

    $header_test = array();
    foreach($headers as &$header) { //passing by reference
        $temp = $header;
        if(array_key_exists($temp,$header_test)) {

            **$header = $header . '_' . $header_test[$header];**
            $header_test[$temp]++; 
            unset($temp);
        } else {
            $header_test[$temp] = 1;
        }
    }

//here is the solution VolkerK suggested and it works:
unset($header);

以下是var_dump输出结果不正确(重复值和通知“&”在标题[6],并且缺少最后一个值):

  ["headers"]=>
  array(7) {
    [0]=>
    string(11) "abc_123_ghi"
    [1]=>
    string(8) "dangdarn"
    [2]=>
    string(6) "oops32"
    [3]=>
    **string(8) "poss_dup"**
    [4]=>
    string(10) "poss_dup_1"
    [5]=>
    string(9) "pos_s_dup"
    [6]=>
    **&string(8) "poss_dup"**
  }
  ["header_test"]=>
  array(6) {
    ["abc_123_ghi"]=>
    int(1)
    ["dangdarn"]=>
    int(1)
    ["oops32"]=>
    int(1)
    ["poss_dup"]=>
    int(2)
    ["pos_s_dup"]=>
    int(1)
    ["bad_chars_3_9"]=>
    int(1)
  }

现在这里有效,即使用$ original_array [$ key] = $ new_value格式:

$header_test = array();
foreach($headers as $key => $header) {
    $temp = $header;
    if(array_key_exists($temp,$header_test)) {

        **$headers[$key] = $header . '_' . $header_test[$header];**
        $header_test[$temp]++; 
        unset($temp);
    } else {
        $header_test[$temp] = 1;
    }
}

的var_dump:

  ["headers"]=>
  array(7) {
    [0]=>
    string(11) "abc_123_ghi"
    [1]=>
    string(8) "dangdarn"
    [2]=>
    string(6) "oops32"
    **[3]=>
    string(8) "poss_dup"
    [4]=>
    string(10) "poss_dup_1"**
    [5]=>
    string(9) "pos_s_dup"
    **[6]=>
    string(13) "bad_chars_3_9"**
  }
  ["header_test"]=>
  array(6) {
    ["abc_123_ghi"]=>
    int(1)
    ["dangdarn"]=>
    int(1)
    ["oops32"]=>
    int(1)
    ["poss_dup"]=>
    int(2)
    ["pos_s_dup"]=>
    int(1)
    ["bad_chars_3_9"]=>
    int(1)
  }

VolkerK提出了解决方案。除了他的其他好建议,关键是在foreach循环之后取消设置$ header。

2 个答案:

答案 0 :(得分:1)

只是拿你的代码

<?php
$headers = array('Abc 123 ghi',
            'dangdarn',
            'oops32',
            'poss dup',
            'poss dup',
            'pos  _s_ dup',
            'bad chars\'& 3% 9'
           );

$header_test = array();
foreach($headers as &$header) { //passing by reference
    $temp = $header;
    if(array_key_exists($temp,$header_test)) {

        $header = $header . '_' . $header_test[$header];
        $header_test[$temp]++; 
        unset($temp);
    } else {
        $header_test[$temp] = 1;
    }
}

var_dump($headers);

产生

array(7) {
  [0]=>
  string(11) "Abc 123 ghi"
  [1]=>
  string(8) "dangdarn"
  [2]=>
  string(6) "oops32"
  [3]=>
  string(8) "poss dup"
  [4]=>
  string(10) "poss dup_1"
  [5]=>
  string(12) "pos  _s_ dup"
  [6]=>
  &string(16) "bad chars'& 3% 9"
}

在我的机器上使用php 5.3.5 / win32。看起来你的“真实”代码在循环中和/或循环之后为$ header做了其他事情。

有点简化(消除$ tmp):

<?php
$headers = getData();
$header_test = array();
foreach($headers as &$header) { //passing by reference
    if( array_key_exists($header, $header_test) ) {
        $header = $header . '_' . $header_test[$header]++;
    }
    else {
        $header_test[$header] = 1;
    }
}

// removes the reference that's causing the & in front
// of the last element of $headers in the output of var_dump
// if $headers can be empty you need to guard this
// to avoid "undefined variable 'header' warning.
// Probably better to put this code in a function
// so that $header can fall out of scope automagically
unset($header); 

var_dump($headers);

function getData() {
    return array('Abc 123 ghi',
        'dangdarn',
        'oops32',
        'poss dup',
        'poss dup',
        'pos  _s_ dup',
        'bad chars\'& 3% 9',
        'poss dup',
        'poss dup'
    );
}

打印

array(9) {
  [0]=>
  string(11) "Abc 123 ghi"
  [1]=>
  string(8) "dangdarn"
  [2]=>
  string(6) "oops32"
  [3]=>
  string(8) "poss dup"
  [4]=>
  string(10) "poss dup_1"
  [5]=>
  string(12) "pos  _s_ dup"
  [6]=>
  string(16) "bad chars'& 3% 9"
  [7]=>
  string(10) "poss dup_2"
  [8]=>
  string(10) "poss dup_3"
}

答案 1 :(得分:-5)

应该是

foreach(&$headers as $header) { //passing by reference

&amp; headers是对数组的引用,$ header是每个成员的句柄。您可以用任何标识符替换句柄。