Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] Jak odczytać dużego CSV ?
Forum PHP.pl > Forum > Przedszkole
Agape
Mam plik csv który zajmuje 140 MB, serwer nie daje rady jest "Out of memory" i zastanawiam sie czy nie da sie jakos partiami wczytywac pliku zeby tylko jego czesc byla w pamieci a pozniej kolejna czesc odczytac i przerobic ? Ewentualnie jakis inny sposob, jestem w kropce, juz 2 godziny nad tym siedze ... w komentarzach w manualu znalazlem klase CsvImporter która niby to robi, ale jak odczytam przed tym plikiem jakis jeszcze wczesniej, to tez mam "Out of memory". Moze daloby sie przerobic jakos ta klase zeby nie zajmowala tyle pamieci ?

  1. <?php
  2. class CsvImporter
  3. {
  4. private $fp;
  5. private $parse_header;
  6. private $header;
  7. private $delimiter;
  8. private $length;
  9. //--------------------------------------------------------------------
  10. function __construct($file_name, $parse_header=false, $delimiter="\t", $length=8000)
  11. {
  12. $this->fp = fopen($file_name, "r");
  13. $this->parse_header = $parse_header;
  14. $this->delimiter = $delimiter;
  15. $this->length = $length;
  16. // $this->lines = $lines;
  17.  
  18. if ($this->parse_header)
  19. {
  20. $this->header = fgetcsv($this->fp, $this->length, $this->delimiter);
  21. }
  22.  
  23. }
  24. //--------------------------------------------------------------------
  25. function __destruct()
  26. {
  27. if ($this->fp)
  28. {
  29. fclose($this->fp);
  30. }
  31. }
  32. //--------------------------------------------------------------------
  33. function get($max_lines=0)
  34. {
  35. //if $max_lines is set to 0, then get all the data
  36.  
  37. $data = array();
  38.  
  39. if ($max_lines > 0)
  40. $line_count = 0;
  41. else
  42. $line_count = -1; // so loop limit is ignored
  43.  
  44. while ($line_count < $max_lines && ($row = fgetcsv($this->fp, $this->length, $this->delimiter)) !== FALSE)
  45. {
  46. if ($this->parse_header)
  47. {
  48. foreach ($this->header as $i => $heading_i)
  49. {
  50. $row_new[$heading_i] = $row[$i];
  51. }
  52. $data[] = $row_new;
  53. }
  54. else
  55. {
  56. $data[] = $row;
  57. }
  58.  
  59. if ($max_lines > 0)
  60. $line_count++;
  61. unset($row);
  62. }
  63. return $data;
  64. }
  65. //--------------------------------------------------------------------
  66.  
  67. }
  68. ?>
Agape
Widziałem to rozwiązanie, tylko problem jest taki że ... nie do końca je rozumiem ... funkcja pobiera część pliku i go obrabia i idzie dalej, to wiem z opisu, ale gdzie wkleic swoj kod. Bylbym wdzieczny za wskazanie miejsca albo wytlumaczenie tego dokladniej
kapslokk
  1. call_user_func_array($callback,array(fread($handle,$chunk_size),&$handle,$i));


Jeśli jako $callback przekazesz nazwe funkcji stworzonej przez siebie to zostanie ona wykonana z parametrami z array(fread($handle,$chunk_size),&$handle,$i))
Damonsson
http://php.net/manual/en/class.splfileobject.php
Forti
Ja parsuje kilka plików .csv po 300-400MB każdy. PHP nie daje rady smile.gif

Zaciągnąłem node.js, wielowątkowość i czas na jeden plik:

PHP - 20-30 minut
node.js - 20-30 sekund

cóż.. nie do wszystkiego PHP się nadaje wink.gif fakt ze tam jest coś więcej niż czytanie, jest jeszcze mapowanie i parsowanie odpowiednio, ale mimo wszystko..
phpion
Nie prościej wrzucić dane do bazy danych (chociażby do tabeli tymczasowej) za pomocą LOAD DATA? Poleci to migusiem, a co potem z tymi danymi zrobisz (przeformatujesz, przeniesiesz do właściwej struktury) to już Twoja sprawa, a całość i tak robisz na poziomie bazy danych.
Agape
Musiałbym to robić ręcznie, a zalozenie jest takie zeby to zautomatyzowac. Mam pare takich plikow ktore musze aktualizowac raz na jakis czas i beda dochodzic nowe.
phpion
Ale co byś musiał robić ręcznie?
DarkAbso
Rozwiązanie, które podał @phpion wygląda najsensowniej (przynajmniej z mojej perspektywy). Od razu również dziękuje za ten pomysł (zoptymalizuje dzięki temu projekt). smile.gif
phpion
Dodam tylko jeszcze, że osobiście z dane z pliku wrzucam do tabeli tymczasowej o wszystkich kolumnach tekstowych. Dopiero potem przekształcam dane (np. w CSV mam liczbę jako 200 000,50 co przekształcam na 200000.50), zmieniam typy kolumn na właściwe (z tekstowych na np. numeryczne), usuwam nieprawidłowości w danych (np. potencjalne naruszenia kluczy obcych) itd. Dopiero mając w ten sposób obrobione dane przenoszę je do tabel właściwych.
Agape
@phpion dzięki za super solucje. W wolnym czasie sprawdzę, ale rozwiazanie wydaje sie najlepsze. Rozumiem ze pozniej z tymczasowej pobierasz dane przez php, obrabiasz i przerzucasz partiami. Super.

@phpion jeszcze raz dzięki za takie rozwiązanie, właśnie je testuje, ale mam problem z jednym. W bazie mam powiedzmy 10 kolumn a w csv powiedzmy 25, jak teraz przypisac odpowiednie wartosci do kolumn ?
  1. LOAD DATA LOCAL INFILE "'.realpath(dirname(__FILE__).'/../../csv/plik.csv').'"
  2. INTO TABLE `tabela_z_csv`
  3. FIELDS TERMINATED BY ";"
  4. OPTIONALLY ENCLOSED BY "\""
  5. LINES TERMINATED BY "\r\n"
  6. IGNORE 1 LINES

nizej moge dac liste kolumn, ale to sa kolumny w bazie danych, moge wiec jakas ominac, ale jak uzywajac SET przypisac odpowiednie kolumny z csv do odpowiednich kolumn do bazy danych ? Troche dla mnie nie zrozumiale uzywanie @ w tym poleceniu :/
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.