2012-04-30 224 views
1

我一直有问题打开和阅读2GB csv文件的内容。每次运行该脚本时,都会耗尽服务器内存(10GB VPS Cloud Server),然后死亡。我制作了一个测试脚本,并想知道是否有人可以看看并确认我在这里没有做任何愚蠢的事情(明智的做法),这会导致看起来很高的内存使用量。我曾与我的托管公司谈过,但他们似乎认为这是一个代码问题。所以只是想知道是否有人可以查看这个并确认代码中没有任何东西会导致这种问题。打开并阅读2GB csv

另外,如果你处理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"; 

回答

5

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

我相信你并不需要把所有的解析线像你现在这样做的记忆。

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

它会让你的代码一次只保存一行内存。

0

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

话虽如此,脚本上有两个内存猪。第一个是foreach,它复制数组。在array_keys上做一个foreach,然后引用数组中的字符串入口来获取行。第二个,是@YourCommonSense引用的一个:你应该设计你的算法,使其在流模式下工作(即不需要将整个数据集存储在内存中)。匆匆一瞥,似乎是可行的。

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; 
    } 
}