使用按引用传递的 Foreach 给出的结果与按值传递不同

发布于 2024-12-14 23:28:15 字数 3522 浏览 2 评论 0 原文

我在 PHP 中编写了几行代码来重命名数组中的重复值(有一些灵感 从这里)。 基本上,我遍历原始数组 ($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 输出,其结果不正确(重复值并注意 headers[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 。

I wrote a few lines of code in PHP to rename duplicate values in an array (with some inspiration from here).
Basically, I go through the original array ($headers[]), while using the keys of another array ($header_test[]) to keep track of duplicates. If there is a duplicate, I change the value of that element in $headers[].

But the weird thing is that I was not getting the right results by passing by reference in the foreach. I had to actually set the values by using the full $array_name[$key] = $new_value format. Why is that?

(Spoiler alert: VolkerK's answer is correct - need to unset $header (the foreach "$value" variable) and then it works.)

Here:

Using this input:

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

Then applying this custom function which I don't think would affect the problem at hand:

    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

Passing the array values by reference doesn't work right (see below for 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);

Here are the var_dump outputs with incorrect results (duplicate values and notice "&" at headers[6], and missing last value):

  ["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)
  }

And now here is what works, which is to use $original_array[$key] = $new_value format:

$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 came up with the solution. Besides his other good suggestions, the key thing was unsetting $header after the foreach loop.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

无声情话 2024-12-21 23:28:15

您的代码即可

<?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"
}

just taking your code

<?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);

produces

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"
}

on my machine using php 5.3.5/win32. Looks like your "real" code does something else to $header in and/or after the loop.

A bit simplified (eliminating $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'
    );
}

prints

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"
}
场罚期间 2024-12-21 23:28:15

应该是

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

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

It should be

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

&headers is the reference to the array and $header is a handle for each member. You could replace the handle with any identifier.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文