2012-04-30 4 views
1

2GB CSV 파일의 내용을 열어 읽는 데 문제가 있습니다. 매번 스크립트를 실행하면 서버 메모리 (10GB VPS 클라우드 서버)가 소모되어 사망합니다. 나는 테스트 스크립트를 만들었고 누군가가 외모를 가질 수 있는지 궁금해서 여기에 어리석은 일 (php wise)을하지 않으면 메모리 사용량이 비정상적으로 높아지게된다. 나는 호스팅 회사와 이야기를 나눴지만 코드 문제라고 생각한다. 아무도이 문제를 살펴보고 이런 종류의 문제를 일으킬만한 코드가 없다는 것을 확인하면 궁금합니다.2GB csv 열기 및 읽기

또한 2GB csv를 처리하는 경우 이전에 이와 같은 문제가 발생 했습니까?

감사

<?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

다른 사람들이 이미 지적했듯이 전체 2GB 파일을 메모리에로드하고 있습니다. 각 행에서 여러 개의 문자열이있는 배열을 만드는 동안 이렇게하면 결과적으로 필요한 메모리가 일반 파일 크기 이상이됩니다.

당신은 예를 들어 키있는 배열로 각 행을 반환 하나, 이상적으로 반복자와 별도로 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; 
    } 
}