如何提高 PHP 中 for 循环的速度?

How to improve the speed of a for loop in PHP?

我有一个包含不同行的 CSV 文件:

;0;1;0;4;5;M;468468;A1101;0090
0;1;0;4;5;M;468468;A1108;0090

并且在照片文件夹中,例如第一个视图的命名格式必须为“A1101_0090-1.JPG”。

我写了一个代码,让你有两个东西:

我的脚本有效,但是当我放入一个包含 5000 多张照片的大照片文件夹时,处理时间很长...我该如何改进我的代码?

<?php
echo '<pre>';
$dataImage = [];
$dataImageTmp = [];
$path = $_POST['path'];

$photos = scandir($path);
$photos = array_map('strtoupper', $photos);

if (($handle = fopen("../RC_PRODUCT_HUB.csv", "r")) !== FALSE) {
    $firstLine = true;
    while (($data = fgetcsv($handle, 9000000, ";")) !== FALSE){
        if (!$firstLine){
            if ($data[0] != null) {
                $countImage = count(glob($path . $data[6] . '_' . $data[7] . '*.*'));
                for ($i = 0; $i <= $countImage; ++$i) {
                    if ((file_exists($fileName = $path.$data[6].'_'.$data[7].'-'.$i.'.JPG'))){
                        if (!in_array($fileName, $dataImage)){
                            $dataImage[$data[6] . '_' . $data[7]]['file'][$i] = $fileName;
                            $fileName = str_replace($path, '', $fileName);
                            if (!in_array($fileName, $dataImageTmp)){
                                $dataImageTmp[] = $fileName;
                            }
                        }
                        $dataImage[$data[6] . '_' . $data[7]]['TOTAL'] = $countImage;
                    }
                }
            }
        }
        $firstLine = false;
    } 

    //FIRST PART
    echo count($dataImage)." refs founds.<br>";
    print_r($dataImage).'<br>';

    //SECOND PART

    $dataImageTmp = array_map('strtoupper', $dataImageTmp); 
    $resultat = array_diff($photos, $dataImageTmp);
    $element = '.';
    unset($resultat[array_search($element, $resultat)]);
    $element2 = '..';
    unset($resultat[array_search($element2, $resultat)]);

    echo count($resultat)." photos found.<br>";

    foreach ($resultat as $result) {
        echo ($result) . '<br>';
    }
}
?>

一些注意事项:

  • 为什么先count(glob(..))然后再循环查找文件名(file_exists)?您只需执行 glob($path . $data[6] . '_' . $data[7] . '*.JPG') 即可获取文件名。您的解决方案首先创建一个包含所有文件名的数组,对它进行计数,然后丢弃它并为文件名创建一个全新的数组。

    您可以遍历 glob 返回的数组,然后根据需要从文件名中提取 $i

  • 如果你需要那个count(glob(..)),你可以用shell命令替换它。我认为它们会更快,因为它们不需要内存 allocation/deallocation in PHP.

    类似于shell_exec("ls '{$path}{$data[6]}_{$data[7]}*.*' | wc -l")。当然,这是针对基于 *nix 的系统和 bash/sh。您可以为其他 OS(或 shell)找到类似的东西。

  • 您可以以某种方式拆分文件,然后使用多个脚本来处理它们。根据您想要的复杂程度,此解决方案可能会有很大差异。喜欢:

    • 预先拆分 csv 文件,然后 运行 在其上编写脚本,然后合并结果。
    • 写一个脚本读取csv,运行多个进程,把csv文件的一部分给每个进程处理,然后合并结果。 Process 或类似的库在这里很有用。
    • 使用作业队列。脚本读取 csv 文件并为每一行创建一个作业(可能不是每一行,但就像每 100 行)。作业由多个工作人员处理,结果保存在数据库或要合并的东西中。有一些解决方案,但我只在 Laravel 或 Symfony 等框架中使用它们,它们有自己的作业队列。搜索 php job queue,您会找到一些解决方案。

不要打电话给 glob()。只需使用一个循环来处理按数字顺序匹配模式的每个文件。当文件不存在时,您可以停止循环。

我假设您的文件名数字序列没有间隔。

if (($handle = fopen("../RC_PRODUCT_HUB.csv", "r")) !== FALSE) {
    fgets($handle); // skip header line
    while (($data = fgetcsv($handle, 9000000, ";")) !== FALSE){
        if ($data[0] != null) {
            for ($i = 1; file_exists($fileName = $path.$data[6].'_'.$data[7].'-'.$i.'.JPG'); ++$i) {
                if (!in_array($fileName, $dataImage)){
                    $dataImage[$data[6] . '_' . $data[7]]['file'][$i] = $fileName;
                    $fileName = str_replace($path, '', $fileName);
                    if (!in_array($fileName, $dataImageTmp)){
                        $dataImageTmp[] = $fileName;
                    }
                }
                if (isset($dataImage[$data[6] . '_' . $data[7]]['TOTAL'])) {
                    $dataImage[$data[6] . '_' . $data[7]]['TOTAL']++;
                } else {
                    $dataImage[$data[6] . '_' . $data[7]]['TOTAL'] = 1;
                }
            }
        }
    }
}