2012-05-24 13 views
6

Używam PHP do importowania danych z pliku CSV za pomocą fgetcsv(), co daje tablicę dla każdego wiersza. Początkowo miałem graniczną zestaw znaków w 1024, tak jak poniżej:Upewnij się, że fgetcsv() odczytuje całą linię

while ($data = fgetcsv($fp, 1024)) { 
    // do stuff with the row 
} 

jednak CSV z 200+ kolumn przekroczyła granicę 1024 w wielu rzędach. To spowodowało zatrzymanie odczytu linii w połowie wiersza, a następnie następne wywołanie fgetcsv() rozpoczęłoby się, gdy poprzednie zostało przerwane i tak dalej, aż do osiągnięcia wartości EOL.

Od tego czasu podniosłem ten limit do 4096, który powinien zająć się większością przypadków, ale chciałbym wprowadzić kontrolę, aby upewnić się, że cała linia została przeczytana po pobraniu każdej linii. Jak mam to zrobić?

Myślałem, aby sprawdzić koniec ostatniego elementu tablicy dla znaków końca wiersza (\ n, \ r, \ r \ n), ale czy nie zostaną one sparsowane przez wywołanie fgetcsv() ?

+0

Ponadto zdaję sobie sprawę, że mogę programowo określić najdłuższą linię w pliku, ale może to być dużo narzutów w naprawdę dużych plikach CSV. Chciałbym dowiedzieć się, jak zapewnić, by każda linia była odczytywana w całości w locie. –

Odpowiedz

1

Dziękuję za sugestie, ale rozwiązania te tak naprawdę nie rozwiązały problemu, wiedząc, że uwzględniamy najdłuższą linię, a jednocześnie zapewniamy limit. Udało mi się to osiągnąć, używając polecenia wc -L UNIX poprzez shell_exec(), aby określić najdłuższą linię w pliku przed rozpoczęciem pobierania linii. Kod jest poniżej:

// open the CSV file to read lines 
$fp = fopen($sListFullPath, 'r'); 

// use wc to figure out the longest line in the file 
$longestArray = explode(" ", shell_exec('wc -L ' . $sListFullPath)); 
$longest_line = (int)$longestArray[0] + 4; // add a little padding for EOL chars 

// check against a user-defined maximum length 
if ($longest_line > $line_length_max) { 
    // alert user that the length of at least one line in the CSV is too long 
} 

// read in the data 
while ($data = fgetcsv($fp, $longest_line)) { 
    // do stuff with the row 
} 

Takie podejście gwarantuje, że każdy wiersz jest czytany w całości i nadal stanowi zabezpieczenie dla bardzo długich kolejkach bez wychodzenia przez cały plik PHP z linii po linii.

6

Po prostu pomiń parametr długości. Jest opcjonalne w PHP5.

3

Po prostu nie określaj limitu, a fgetcsv() będzie sapał tyle, ile potrzeba, aby uchwycić pełną linię. Jeśli określisz limit, całkowicie zależy to od Ciebie, aby przeskanować strumień plików i upewnić się, że nie kroisz czegoś na środku.

Należy jednak pamiętać, że nieokreślenie limitu może być ryzykowne, jeśli nie masz kontroli nad generowaniem tego .csv w pierwszej kolejności. Byłoby łatwo opróżnić serwer złośliwym plikiem CSV, który ma wiele terabajtów danych w jednym wierszu.

+0

Rozważałem to, ale 2 rzeczy: 1) NIE mam kontroli nad generacją CSV. Są one dostarczane przez (niewiarygodnych) klientów, więc naprawdę chcę narzucić jakiś limit. 2) Instrukcja mówi "Pomijając ten parametr (lub ustawiając go na 0 w PHP 5.0.4 i późniejszych) maksymalna długość linii nie jest ograniczona, co jest nieco wolniejsze." Boję się tego, co "trochę wolniej" doda do pliku CSV, który ma 100k + wiersze. –

+2

nieznacznie wolniej = odczytuje plik w porcjach, aż znajdzie miejsce gdzieś w tym fragmencie, a następnie przewija wskaźnik pliku, aby następny odczyt pojawił się tuż po przerwie. –

+1

MOŻESZ wykonać osobny wiersz po wierszu oddzielnie, a następnie użyć [str_get_csv()] (http://php.net/manual/en/function.str-getcsv.php) do wykonania analizy parsowania csv-> . –

0

Byłbym ostrożny z ostatecznym rozwiązaniem. Udało mi się przesłać plik o nazwie /.;ls -a;.csv w celu wykonania iniekcji polecenia. Upewnij się, że zweryfikujesz ścieżkę pliku, jeśli używasz tego podejścia. Poza tym dobrym pomysłem może być dostarczenie default_length w przypadku, gdy twoja wc ulegnie awarii z jakiegokolwiek powodu.

// use wc to find max line length 
// uses a hardcoded default if wc fails 
// this is relatively safe from command 
// injection since the file path is a tmp file 
$wc = explode(" ", shell_exec('wc -L ' . $validated_file_path)); 
$longest_line = (int)$wc[0]; 
$length = ($longest_line) ? $longest_line + 4 : $default_length; 
Powiązane problemy