【问题标题】:How can I parse, sort, and print a 90MB JSON file with 100,000 records to CSV?如何将包含 100,000 条记录的 90MB JSON 文件解析、排序和打印到 CSV?
【发布时间】:2020-01-10 06:45:08
【问题描述】:

背景

我正在尝试完成一个代码挑战,我需要重构一个简单的 PHP 应用程序,该应用程序接受人员的 JSON 文件,按注册日期对其进行排序,然后将它们输出到 CSV 文件。所提供的程序已经在运行,并且可以在少量输入下正常工作,但在大量输入下会故意失败。为了完成挑战,应该修改程序,使其能够解析和排序 100,000 条记录、90MB 的文件,而不会像现在一样耗尽内存。

在当前状态下,程序使用file_get_contents(),然后是json_decode(),然后是usort() 对项目进行排序。这适用于 small 示例数据文件,但不适用于 large 示例数据文件 - 内存不足。

输入文件

该文件为 JSON 格式,包含 100,000 个对象。每个对象都有一个 registered 属性(示例值 2017-12-25 04:55:33),这就是 CSV 文件中的记录应按升序排序的方式。

我尝试的解决方案

目前,我使用了halaxa/json-machine 包,并且能够遍历文件中的每个对象。例如

$people = \JsonMachine\JsonMachine::fromFile($fileName);
foreach ($people as $person) {
    // do something
}

将整个文件作为 PHP 数组读入内存不是一种选择,因为它占用了太多内存,所以到目前为止我能想到的唯一解决方案是遍历文件中的每个对象,找到注册日期最早的人并打印出来。然后,再次遍历整个文件,找到注册日期最早的下一个人并打印出来等等。

其中的问题在于嵌套循环:一个运行 100,000 次的循环包含一个运行 100,000 次的循环。这不是一个可行的解决方案,这是我所做的最远的。

如何解析、排序和打印到 CSV,一个包含 100,000 条记录的 JSON 文件?允许使用包/服务。

【问题讨论】:

  • 我的意思是..挑战的全部意义在于自己做..如果您要向其他人寻求答案,您为什么还要这样做?
  • 我举个例子,你想要什么? Json 写成 CSV 吗?
  • @CrayonViolent 因为...我不知道该怎么做?而不仅仅是放弃,我想扩展我的知识
  • 也许like this json stream parser 会有所帮助。
  • @YvesLeBorg 我正在使用一个不同的、类似的包,但我如何使用它来对项目进行分类?因为每个对象都需要根据一个registered属性进行排序。

标签: php json performance sorting memory


【解决方案1】:

我最终以块的形式导入 MongoDB,然后以正确的顺序检索打印

导入示例:

$collection = (new Client($uri))->collection->people;
$collection->drop();

$people = JsonMachine::fromFile($fileName);

$chunk = [];
$chunkSize = 5000;
$personNumber = 0;
foreach ($people as $person) {
    $personNumber += 1;
    $chunk[] = $person;
    if ($personNumber % $chunkSize == 0) { // Chunk is full
        $this->collection->insertMany($chunk);
        $chunk = [];
    }
}
// The very last chunk was not filled to the max, but we still need to import it
if(count($chunk)) {
    $this->collection->insertMany($chunk);
}
// Create an index for quicker sorting
$this->collection->createIndex([ 'registered' => 1 ]);

检索示例:

$results = $this->collection->find([],
    [
        'sort' => ['registered' => 1],
    ]
);

// For every person...
foreach ($results as $person) {
    // For every attribute...
    foreach ($person as $key => $value) {
        if($key != '_id') { // No need to include the new MongoDB ID
            echo some_csv_encode_function($value) . ',';
        }
    }
    echo PHP_EOL;
}

【讨论】:

    猜你喜欢
    • 2012-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-19
    • 1970-01-01
    • 2017-09-02
    • 1970-01-01
    相关资源
    最近更新 更多