打开并阅读2GB csv

时间:2012-04-30 18:44:26

标签: php csv

我在打开和阅读2gb csv文件的内容时遇到了问题。每次我运行脚本都会耗尽服务器内存(10GB VPS云服务器)然后被杀死。我已经制作了一个测试脚本,并想知道是否有人可以看看并确认我没有做任何愚蠢的事情(php明智),这将导致看起来和非常大量的内存使用量。我已经和我的托管公司谈过,但他们似乎认为这是一个代码问题。所以只是想知道是否有人可以查看这个并确认代码中没有任何内容会导致此类问题。

另外,如果你处理2GB的csvs,你之前有没有这样的东西?

由于

<?php

ini_set("memory_limit", "10240M");

$start = time();
echo date("Y-m-d H:i:s", $start)."\n";

$file = 'myfile.csv';

$lines = $keys = array();
$line_count = 0;
$csv = fopen($file, "r");

if(!empty($csv))
{
    echo "file open \n";

    while(($csv_line = fgetcsv($csv, null, ',', '"')) !== false)
    {
        if($line_count==0) {
            foreach($csv_line as $item) {
                $keys[] = preg_replace("/[^a-zA-Z0-9]/", "", $item);    
            }
        } else {
            $array = array();
            for ($i = 0; $i <count($csv_line); $i++) {
                $array[$keys[$i]] =  $csv_line[$i]; 
            }
            $lines[] = (object) $array;

            //print_r($array);
            //echo "<br/><br/>";
        }
        $line_count++;
    }

    if ($line_count == 0) {
        echo "invalid csv or wrong delimiter / enclosure ".$file;
    }

} else {
    echo "cannot open ".$file;
}
fclose ($csv);

echo $line_count . " rows \n";

$end = time();
echo date("Y-m-d H:i:s", $end)."\n";

$time = number_format((($end - $start)/60), 2);

echo $time."\n";

echo "peak memory usages ".memory_get_peak_usage(true)."\n";

3 个答案:

答案 0 :(得分:5)

它实际上不是一个“开放”问题,而是处理问题

我确信您不需要像目前那样将所有已解析的行保留在内存中。

为什么不把解析后的行放在它所属的任何地方 - 数据库或其他文件或其他什么?

它会使你的代码一次只保留在内存中。

答案 1 :(得分:2)

正如其他人已经指出的那样,您将整个2 GB文件加载到内存中。你在创建一个包含每行中多个字符串的数组时执行此操作,因此事实上所需的内存大于普通文件大小。

您可能希望单独处理CSV文件的每一行,理想情况下使用迭代器,例如将每行作为键控数组返回:

$csv = new CSVFile('../data/test.csv');

foreach ($csv as $line) {
    var_dump($line);
}

此处的示例输出:

array(3) {
  ["Make"]=> string(5) "Chevy"
  ["Model"]=> string(4) "1500"
  ["Note"]=> string(6) "loaded"
}
array(3) {
  ["Make"]=> string(5) "Chevy"
  ["Model"]=> string(4) "2500"
  ["Note"]=> string(0) ""
}
array(3) {
  ["Make"]=> string(5) "Chevy"
  ["Model"]=> string(0) ""
  ["Note"]=> string(6) "loaded"
}

这个迭代器的灵感来自一个名为SPLFileObject的PHP内置程序。由于这是一个迭代器,您可以决定对每行/行的数据执行的操作。请参阅相关问题:Process CSV Into Array With Column Headings For Key

class CSVFile extends SplFileObject
{
    private $keys;

    public function __construct($file)
    {
        parent::__construct($file);
        $this->setFlags(SplFileObject::READ_CSV);
    }

    public function rewind()
    {
        parent::rewind();
        $this->keys = parent::current();
        parent::next();
    }

    public function current()
    {
        return array_combine($this->keys, parent::current());
    }

    public function getKeys()
    {
        return $this->keys;
    }
}

答案 2 :(得分:0)

PHP真的是错误的语言。字符串操作通常会导致在内存中分配字符串的副本,并且只有在脚本结束时才会发生垃圾收集,而实际上不再需要它。如果您知道如何操作,并且它适合执行环境,那么使用perl或sed / awk会更好。

说到这里,脚本上有两个记忆猪。第一个是foreach,它复制数组。在array_keys上执行foreach,并返回数组中的字符串条目以获取行。第二个是@YourCommonSense引用的:您应该设计算法,使其在流模式下工作(即不需要在内存中存储完整数据集)。粗略地看一眼,似乎是可行的。