递归地将数值转换为整数或浮点数

3

由于MYSQL的"SELECT"将整数和浮点数选为字符串,而我需要从JS获取的每个响应都符合正确的数据模型 -

  • 1 不是 "1",
  • 53.2 不是 "53.2",

我创建了这个递归函数,可以处理混合类型 - 数组/对象:

private function cast_number(&$mixed) {
    if(is_array($mixed)) {
        foreach ($mixed as $key => $val)
            if (is_numeric($val))
                $mixed[$key] = (double)$val;
            else if (is_array($val) || is_object($val))
                $mixed[$key] = $this->cast_number($val);
    } else if(is_object($mixed)) {
        foreach ($mixed as $key => $val)
            if (is_numeric($val))
                $mixed->$key = (double)$val;
            else if (is_array($val) || is_object($val))
                $mixed->$key = $this->cast_number($val);
    }
    return $mixed;
}

这是一个相当简单的函数 - 如果是数字,就转换成双精度浮点型,如果是数组或对象,则递归处理。

所有内容都已经放在了正确的位置。

但有两个问题: - 在6MB大小的数据中,大多数数字表示为字符串,需要0.5秒左右 - 在200MB的数据上(是的,我需要它,请不要关注这一点),几分钟后(通常是几秒钟),它会失败并显示需要超过4GB的内存...

  1. 如何改进此函数?(速度、内存)
  2. 为什么它需要这么长时间?即使json_encode这样的更大的函数花费的时间也要少得多...

你尝试过类型转换吗?例如 $mixed[$key] = 0 + $val; - Ivan Gabriele
@IvanGabriele 还没有。我不明白它如何帮助速度?当然,大部分时间内内存将会是4字节而不是8字节。我现在正在尝试。 - Amit
你的问题还有另一个解决方案,就是在服务器上更新mysqlnd,这样PHP就可以从MySQL获取正确的数据类型...这里有更多信息 - M. Eriksson
你可能还想与http://codereview.stackexchange.com/的人交流一下。 - Jeff Puckett
@Amit,我给了你一个完整的答案,展示了不同的时间,正如你所看到的,如果与引用变量结合使用,它可以显著降低执行时间。 - Ivan Gabriele
我对问题出在哪里感到困惑。我经常在PHP中的MySQL中混合使用字符串和数字,但很少遇到麻烦。INT可以轻松地接受"123"。PHP在大多数情况下会自动将字符串转换为数字。请提供一个简短的代码片段来说明这个问题。 - Rick James
2个回答

2
由于强制转换曾经比类型转换更快,我运行了以下代码来计算PHP 7的时间:

因为强制转换曾经比类型转换更快,我运行了这段代码来计算PHP 7的时间:

function getTime($start) {
    return round((microtime(true) - $start) * 1000000) / 1000;
}

function mockData($length) {
    $data = [];
    $i = -1;

    while ($i++ < $length) {
        $data[$i] = strval(rand(1, 10000) / 100);
    }

    return $data;
}

$data = mockData(100000);

// Let's check that they are string before
echo gettype($data[0]) . '<br><br>';

$start = microtime(true);
$convertedData = [];
foreach ($data as $key => $value) {
    $convertedData[$key] = (double) $value;
}
echo '(double) cast took ' . getTime($start) . ' ms.<br>';

$start = microtime(true);
$convertedData = [];
foreach ($data as $key => $value) {
    $convertedData[$key] = 0 + $value;
}
echo 'Coercion took ' . getTime($start) . ' ms.<br>';

我的结果如下:
(double) cast took 27.508 ms.
Coercion took 28.789 ms.

结论

使用floatval(第三种实现字符串转换为双精度浮点数的方法)会更加繁琐,因此在PHP中你无法找到更好的解决方案。你试图实现的是一个脚本操作,不应该用于Web应用程序的正常后端操作。

但是如果你仍然想这样做,可以在php.ini文件中提高memory_limit只要不使用-1这个变通方法

更新

我忘记了一种可能的优化方法,即通过引用传递变量,至少可以执行立即赋值:

$start = microtime(true);
foreach ($data as $key => $value) {
    $data[$key] = (double) $value;
}
echo getTime($start) . ' ms.<br>';

=> 34.018毫秒。

$start = microtime(true);
foreach ($data as &$value) {
    $value = (double) $value;
}
echo getTime($start) . ' ms.<br>';

=> 17.081毫秒。

显然,使用按引用的强制转换可以获得更好的结果:

$start = microtime(true);
foreach ($data as &$value) {
    $value = 0 + $value;
}
echo getTime($start) . ' ms.<br>';

=> 13.1 ms.


这些比较非常好!但它可能是在PHP7上..我忘了提到,我使用的是5.5.34(无法升级),这里有一个例子:http://sandbox.onlinephpfunctions.com/code/5b261993efbc969cfd7c4df9f03a1b5bb0d6b460 - 它只需要0.35秒,并且看起来非常优化..现在似乎不会占用太多内存,这很棒。 - Amit
@Amit,这个函数和我在13.1毫秒内找到的那个完全一样...无论如何,它都不会解决你的内存问题,因为你的原始数据大小太大了。你仍然需要提高memory_limit。你应该阅读这篇文章来了解PHP中内存分配的工作原理。 - Ivan Gabriele

1

这应该会提高速度和内存:

private function cast_number(&$mixed)
{
    foreach ($mixed as &$val) {
        if (is_numeric($val)) {
            $val = (double) $val;
        } else if (is_array($val) || is_object($val)) {
            $this->cast_number($val);
        }
    }
    // do not return $mixed
}

但也许你可以使用array_walk_recursive函数来获得更大的改进。


虽然这个方法可行,但它并没有显著改变执行时间。此外,array_walk_recursive无法处理对象。 - Amit
这就是为什么我没有使用array_walk_recursive提供解决方案的原因;它实际上部分可以,如果你的对象实现了ArrayAccess - godvsdeity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接