2015-03-26 15 views
5

Mam kilka ogromnych plików (11mb i 54mb), które muszę przeczytać, aby przetworzyć resztę skryptu. Obecnie czytam pliki i zapisuję je w tablicy tak:Skuteczne odczytywanie dużych plików tekstowych

$pricelist = array(); 
$fp = fopen($DIR.'datafeeds/pricelist.csv','r'); 
while (($line = fgetcsv($fp, 0, ",")) !== FALSE) { 
    if ($line) { 
     $pricelist[$line[2]] = $line; 
    } 
} 
fclose($fp); 

.. ale ciągle otrzymuję wiadomości o przeciążeniu pamięci z mojego hosta. Jak mogę go czytać bardziej efektywnie?

Nie muszę przechowywać wszystkiego, mam już słowo kluczowe, które dokładnie pasuje do klucza tablicy $line[2] i potrzebuję przeczytać tylko jedną tablicę/linię.

+0

import do db prawdopodobnie –

+0

Pliki są aktualizowane codziennie, czasami kolumny są również przenoszone, więc nie jest idealny do korzystania z DB .. przynajmniej w tej chwili. – 3zzy

+2

Ten plik jest obecnie czytany wiersz po wierszu. Problem polega na wykorzystaniu pamięci w samej macierzy i nie polega na "odczytywaniu" pliku. Musisz zmienić logikę w taki sposób, aby * nie musiał ładować wszystkich danych naraz *, uzyskać hosta z większym limitem pamięci lub użyć pamięci dodatkowej (takiej jak DB, pamięć podręczna poza procesami lub inne pliki) dla danych tymczasowych/przetwarzania, itp. – user2864740

Odpowiedz

5

Jeśli znasz klucz, dlaczego nie odfiltrować według klucza? Możesz sprawdzić użycie pamięci za pomocą funkcji memory_get_usage(), aby sprawdzić, ile pamięci zostało przydzielone po wypełnieniu tablicy $ cennika.

echo memory_get_usage() . "\n"; 
$yourKey = 'some_key'; 
$pricelist = array(); 
$fp = fopen($DIR.'datafeeds/pricelist.csv','r'); 
while (($line = fgetcsv($fp, 0, ",")) !== FALSE) { 
    if (isset($line[2]) && $line[2] == $yourKey) { 
     $pricelist[$line[2]] = $line; 
     break; 
     /* If there is a possiblity to have multiple lines 
     we can store each line in a separate array element 
     $pricelist[$line[2]][] = $line; 
     */ 
    } 
} 
fclose($fp); 
echo memory_get_usage() . "\n"; 
+0

Nie zapomnij się wydostać, gdy już masz swoją linię - nie ma sensu przeglądanie pozostałej części pliku, jeśli masz już potrzebne informacje. – Eborbob

+0

Tak, masz rację @Eborbob, jeśli jesteśmy pewni, że istnieje tylko jedna linia, która działa dla nas, możemy na pewno przerwać na chwilę. – Ugur

0

można upuścić

if ($line) { 

że jedynie powtarza czek od warunku pętli. Jeśli twój plik ma 54 MB, a zachowasz każdą linię z pliku, jako tablicę, a także klucz z kolumny 3 (która jest mieszana do wyszukiwania) ... Widzę, że potrzeba 75-85 MB, aby przechowywać wszystkie w pamięci. To niewiele. Większość stron wordpress lub magento używających widgetów ma 150-200 MB. Ale jeśli twój komputer jest ustawiony na niskim poziomie, może to być problem.

Możesz spróbować odfiltrować niektóre wiersze, zmieniając wartość if ($ line) na if ($ line [1] == 'book'), aby zmniejszyć ilość przechowywanych danych. Ale jedynym pewnym sposobem na przechowywanie tak dużej ilości treści w pamięci jest posiadanie tak dużej ilości pamięci dostępnej dla skryptu.

2

Można spróbować to (ja nie sprawdziłem, czy działa poprawnie)

$data = explode("\n", shell_exec('cat filename.csv | grep KEYWORD')); 

Dostaniesz wszystkie linie zawierające słowo kluczowe, każdą linię jako element tablicy.

Daj mi znać, jeśli to pomoże.

+0

Bardzo dobre rozwiązanie. shell_exec zwraca białą spację (PHP 5.5.9, Linux Mint), jeśli otoczysz shell_exec przycinkiem, wygląda o wiele lepiej. '$ data = explode (" \ n ", przycinanie (shell_exec (" cat filename.csv | grep KEYWORD ")));' Istnieje również wada, większość firm hostingowych nie pozwala klientom na uruchamianie shell_exec we wspólnym envorinment. – Ugur

2

łączę co user2864740 powiedział: „Problemem jest wykorzystanie w pamięci spowodowane przez sam zespół i nie chodzi tu o«czytanie»plik

moje rozwiązanie to:

  • podzielić `array $ priceList`
  • obciążenia tylko 1 raz rozdzielonego na tablicy w pamięci
  • zachować inne podzielony tablic w pliku pośrednim

N.B: i nie sprawdzić, co pisałem

<?php 
    define ("MAX_LINE",   10000) ; 
    define ("CSV_SEPERATOR", ',') ; 

    function intermediateBuilder ($csvFile, $intermediateCsvFile) { 
     $pricelist    = array(); 
     $currentLine   = 0; 
     $totalSerializedArray = 0; 

     if (!is_file()) { 
      throw new Exception ("this is not a regular file: " . $csv); 
     } 
     $fp = fopen ($csvFile, 'r'); 
     if (!$fp) { 
      throw new Exception ("can not read this file: " . $csv); 
     } 

     while (($line = fgetcsv($fp, 0, CSV_SEPERATOR)) !== FALSE) { 
      if ($line) { 
       $pricelist[$line[2]] = $line; 
      } 
      if (++$currentLine == MAX_LINE) { 
       $fp2 = fopen ($intermediateCsvFile, 'a'); 
       if (!$fp) throw new Exception ("can not write in this intermediate csv file: " . $intermediateCsvFile); 

       fputs ($fp2, serialize ($pricelist) . "\n"); 
       fclose ($fp2); 

       unset ($pricelist); 
       $pricelist  = array(); 
       $currentLine = 0; 
       $totalSerializedArray++; 
      } 
     } 
     fclose($fp); 
     return $totalSerializedArray; 
    } 

    /** 
     * @param array : by reference unserialized array 
     * @param integer : the array number to read from the intermediate csv file; start from index 1 
     * @param string : the (relative|absolute) path/name of the intermediate csv file 
     * @throw Exception 
     */ 
    function loadArray (&$array, $arrayNumber, $intermediateCsvFile) { 
     $currentLine = 0; 
     $fp    = fopen ($intermediateCsvFile, 'r'); 
     if (!$fp) { 
      throw new Exception ("can not read this intermediate csv file: " . $intermediateCsvFile); 
     } 
     while (($line = fgetcsv($fp, 0, CSV_SEPERATOR)) !== FALSE) { 
      if (++$currentLine == $arrayNumber) { 
       fclose ($fp); 
       $array = unserialize ($line); 
       return; 
      } 
     } 
     throw new Exception ("the array number argument [" . $arrayNumber . "] is invalid (out of bounds)"); 
    } 

Przykład użycia

try { 
    $totalSerializedArray = intermediateBuilder ($DIR . 'datafeeds/pricelist.csv', 
               $DIR . 'datafeeds/intermediatePricelist.csv'); 

    $priceList = array() ; 
    $arrayNumber = 1; 

    loadArray ($priceList, 
       $arrayNumber, 
       $DIR . 'datafeeds/intermediatePricelist.csv'); 
    if (!array_key_exists ($key, $priceList)) { 
     if (++$arrayNumber > $totalSerializedArray) $arrayNumber = 1; 
     loadArray ($priceList, 
        $arrayNumber, 
        $DIR . 'datafeeds/intermediatePricelist.csv'); 
    } 
    catch (Exception $e) { 
     // TODO : log the error ... 
    } 
0

można spróbować ustawić większą pamięć przy użyciu tego produktu. Możesz zmienić limit, jak chcesz.

ini_set('memory_limit', '2048M'); 

Ale także zawiera informacje o tym, jak chcesz korzystać z tego skryptu.

Powiązane problemy