为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

PHP
千巷猫影 2021-06-01 13:01:42
我正在寻找一种算法策略。我有一个包含 162 列和 55000 行的 csv 文件。我想用一个日期(位于第 3 列)对数据进行排序。首先,我尝试直接将所有内容放入数组中,但内存爆炸了。所以我决定: 1/ 将前 3 列放入一个数组中。2/ 使用 usort 订购此数组 3/ 读取 csv 文件以恢复其他列 4/ 在新的 csv 文件中添加完整的行 5/ 用读取的 csv 文件中的空字符串替换该行//First read of the filewhile(($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 dateusort(    $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);}这个算法真的太长而无法执行,但有效知道如何改进它吗?
查看完整描述

3 回答

?
森林海

TA贡献2011条经验 获得超2个赞

我只在一个小文件上尝试过这个,但原理与你读取文件、存储日期然后对其进行排序的想法非常相似。然后读取原始文件并写出排序后的数据。


在这个版本中,负载只是读取日期并创建一个数组,该数组保存日期和文件中行开头的位置(ftell()每次读取后使用以获取文件指针)。


然后对这个数组进行排序(因为日期首先只使用普通排序)。


然后它遍历已排序的数组,对于每个条目,它用于fseek()定位文件中的记录并读取该行(使用fgets())并将该行写入输出文件...


$file = "a.csv";

$out = "sorted.csv";


$handle = fopen($file, "r");

$tabligne = [];

$start = 0;

while ( $data = fgetcsv($handle) )    {

    $tabligne[] = ['date' => DateTime::createFromFormat('d/m/Y', $data[2]),

        'start' => $start ];

    $start = ftell($handle);

}


sort($tabligne);


$outHandle = fopen( $out, "w" );

foreach ( $tabligne as $entry ) {

    fseek($handle, $entry['start']);

    $copy = fgets($handle);

    fwrite($outHandle, $copy);

}


fclose($outHandle);

fclose($handle);


查看完整回答
反对 回复 2021-06-04
?
侃侃尔雅

TA贡献1801条经验 获得超16个赞

假设您仅限于使用 PHP,并且不能按照评论中的建议使用数据库来实现它,那么下一个最佳选择是使用外部排序算法。

  1. 将文件拆分为小文件。这些文件应该足够小,以便在内存中对它们进行排序。

  2. 在内存中单独对所有这些文件进行排序。

  3. 通过比较每个文件的第一行,将排序后的文件合并为一个大文件。

排序文件的合并可以非常节省内存:在任何给定时间,您只需要在内存中保存每个文件的第一行。具有最小时间戳的第一行应转到结果文件。

对于非常大的文件,您可以级联合并,即:如果您有 10,000 个文件,您可以先合并 100 个文件的组,然后合并生成的 100 个文件。


例子

为了便于阅读,我使用逗号来分隔值而不是换行符。

未排序的文件(想象它太大而无法放入内存):

1, 6, 2, 4, 5, 3

将文件分成足够小以适合内存的部分:

1, 6, 2
4, 5, 3

分别对它们进行排序:

1, 2, 6
3, 4, 5

现在合并:

  1. 比较 1 & 3 → 取 1

  2. 比较 2 & 3 → 取 2

  3. 比较 6 & 3 → 取 3

  4. 比较 6 & 4 → 取 4

  5. 比较 6 & 5 → 取 5

  6. 取 6。


查看完整回答
反对 回复 2021-06-04
  • 3 回答
  • 0 关注
  • 149 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信