2012-04-30 3 views
1

У меня возникли проблемы с открытием и чтением содержимого файла csv 2gb. Каждый раз, когда я запускаю скрипт, он исчерпывает память серверов (10 ГБ VPS Cloud Server), а затем убивается. Я сделал тестовый скрипт и задавался вопросом, может ли кто-нибудь взглянуть и подтвердить, что я не делаю ничего глупого (php wise) здесь, что могло бы вызвать то, что кажется, и невероятно высокий объем использования памяти. Я говорил с моей хостинговой компанией, но они, похоже, считают, что это проблема с кодом. Поэтому просто интересно, может ли кто-нибудь просмотреть это и подтвердить, что в коде нет ничего, что могло бы вызвать такую ​​проблему.Открытие и чтение 2GB csv

Также, если вы имеете дело с 2GB csvs, вы уже видели что-то подобное?

Благодаря

Tim

<?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, который копирует массив. Сделайте foreach на array_keys и вернитесь к строковой записи в массиве, чтобы получить строки. Второй - это тот, который указан @YourCommonSense: вы должны разработать свой алгоритм, чтобы он работал в потоковом режиме (т. Е. Не требовал хранения полного набора данных в памяти). На беглом взгляде это кажется выполнимым.

2

Как уже указывалось, вы загружаете весь файл объемом 2 ГБ в память. Вы делаете это при создании массива с несколькими строками из каждой строки, так что фактически необходимая результирующая память больше, чем простой размер файла.

Вы можете обрабатывать каждую строку 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" 
} 

Этот итератор вдохновлен тем, что встроено в PHP под названием SPLFileObject. Поскольку это итератор, вы решаете, что вы делаете с данными каждой строки/строки. См. Соответствующий вопрос: 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; 
    } 
} 
Смежные вопросы