如何用php订购一个大的csv文件?

我正在寻找一种算法策略。我有一个包含 162 列和 55000 行的 csv 文件。我想用一个日期(位于第 3 列)对数据进行排序。


首先,我尝试直接将所有内容放入数组中,但内存爆炸了。


所以我决定: 1/ 将前 3 列放入一个数组中。2/ 使用 usort 订购此数组 3/ 读取 csv 文件以恢复其他列 4/ 在新的 csv 文件中添加完整的行 5/ 用读取的 csv 文件中的空字符串替换该行


//First read of the file

while(($data = fgetcsv($handle, 0,';')) !== false)

{

    $tabLigne[$columnNames[0]] = $data[0];

    $tabLigne[$columnNames[1]] = $data[1];

    $tabLigne[$columnNames[2]] = $data[2];


    $dateCreation = DateTime::createFromFormat('d/m/Y', $tabLigne['Date de Création']);




    if($dateCreation !== false)

    {

        $tableauDossiers[$row] = $tabLigne;

    }

    $row++; 


    unset($data);

    unset($tabLigne);

}


//Order the array by date


usort(

    $tableauDossiers,

    function($x, $y) {

        $date1 = DateTime::createFromFormat('d/m/Y', $x['Date de Création']);

        $date2 = DateTime::createFromFormat('d/m/Y', $y['Date de Création']);


        return $date1->format('U')> $date2->format('U');


    }

);



fclose($handle);

copy(PATH_CSV.'original_file.csv', PATH_CSV.'copy_of_file.csv');




for ($row = 3; $row <= count($tableauDossiers); $row++)


{

    $handle = fopen(PATH_CSV.'copy_of_file.csv', 'c+');

    $tabHandle = file(PATH_CSV.'copy_of_file.csv');

    fgetcsv($handle);

    fgetcsv($handle);

    $rowHandle = 2;

    while(($data = fgetcsv($handle, 0,';')) !== false)

    {


        if($tableauDossiers[$row]['Caisse Locale Déléguée'] == $data[0]

                && $tableauDossiers[$row]['Date de Création'] == $data[1]

                && $tableauDossiers[$row]['Numéro RCT'] == $data[2])

        {

            fputcsv($fichierSortieDossier, $data,';');

            $tabHandle[$rowHandle]=str_replace("\n",'', $tabHandle[$rowHandle]);

            file_put_contents(PATH_CSV.'copy_of_file.csv', $tabHandle);

            unset($tabHandle);


            break;

        }

        $rowHandle++;

        unset($data);

        unset($tabLigne);

    }


    fclose($handle);

    unset($handle);

}

这个算法真的太长而无法执行,但有效


知道如何改进它吗?


千巷猫影
浏览 150回答 3
3回答

森林海

我只在一个小文件上尝试过这个,但原理与你读取文件、存储日期然后对其进行排序的想法非常相似。然后读取原始文件并写出排序后的数据。在这个版本中,负载只是读取日期并创建一个数组,该数组保存日期和文件中行开头的位置(ftell()每次读取后使用以获取文件指针)。然后对这个数组进行排序(因为日期首先只使用普通排序)。然后它遍历已排序的数组,对于每个条目,它用于fseek()定位文件中的记录并读取该行(使用fgets())并将该行写入输出文件...$file = "a.csv";$out = "sorted.csv";$handle = fopen($file, "r");$tabligne = [];$start = 0;while ( $data = fgetcsv($handle) )&nbsp; &nbsp; {&nbsp; &nbsp; $tabligne[] = ['date' => DateTime::createFromFormat('d/m/Y', $data[2]),&nbsp; &nbsp; &nbsp; &nbsp; 'start' => $start ];&nbsp; &nbsp; $start = ftell($handle);}sort($tabligne);$outHandle = fopen( $out, "w" );foreach ( $tabligne as $entry ) {&nbsp; &nbsp; fseek($handle, $entry['start']);&nbsp; &nbsp; $copy = fgets($handle);&nbsp; &nbsp; fwrite($outHandle, $copy);}fclose($outHandle);fclose($handle);

侃侃尔雅

假设您仅限于使用 PHP,并且不能按照评论中的建议使用数据库来实现它,那么下一个最佳选择是使用外部排序算法。将文件拆分为小文件。这些文件应该足够小,以便在内存中对它们进行排序。在内存中单独对所有这些文件进行排序。通过比较每个文件的第一行,将排序后的文件合并为一个大文件。排序文件的合并可以非常节省内存:在任何给定时间,您只需要在内存中保存每个文件的第一行。具有最小时间戳的第一行应转到结果文件。对于非常大的文件,您可以级联合并,即:如果您有 10,000 个文件,您可以先合并 100 个文件的组,然后合并生成的 100 个文件。例子为了便于阅读,我使用逗号来分隔值而不是换行符。未排序的文件(想象它太大而无法放入内存):1,&nbsp;6,&nbsp;2,&nbsp;4,&nbsp;5,&nbsp;3将文件分成足够小以适合内存的部分:1,&nbsp;6,&nbsp;24,&nbsp;5,&nbsp;3分别对它们进行排序:1,&nbsp;2,&nbsp;63,&nbsp;4,&nbsp;5现在合并:比较 1 & 3 → 取 1比较 2 & 3 → 取 2比较 6 & 3 → 取 3比较 6 & 4 → 取 4比较 6 & 5 → 取 5取 6。
打开App,查看更多内容
随时随地看视频慕课网APP