Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PLIKI] Odczytanie X ostatnich linii pliku
Forum PHP.pl > Forum > PHP
kiler129
Witajcie!
Skrypt który pisze ma dość specyficzne środowisko uruchomieniowe - nieco ponad 150Mhz procesora ARM i 80MB ramu.
Początkowo wykonywałem komendę "tail -n 4 plik" jednakże w pętli jest to mało efektywne. Do tego stopnia, że dla 10 elementów zajmuje to ok 2 sekund (jest to nie do zaakceptowania w moim przypadku).
Przez ostatnią godzinę krążę w kółko - fopen, fgets itp. Niestety nie wiem jak pobrać ostanie X linii z pliku. Pobranie z początku nie sprawia mi problemu:
  1. function unix_head($f, $n) { //Pobiera n linii z początku pliku
  2. $fp = @fopen($f, "r");
  3. $ret = "";
  4. $i = 0;
  5. while(!feof($fp) && $i<$n) {
  6. $ret .= fgets($fp);
  7. $i++;
  8. }
  9. fclose($fp);
  10. return $ret;
  11. }

Mogę co prawda użyć funkcji file() i pobawić się tablicą jednakże ładowanie do pamięci całego pliku (od 100KB do 1MB) nie ma nic wspólnego z wydajnością dry.gif


Czy któryś z kolegów może podać mi sposób na implementację funkcji tail w php?
IceManSpy
Tutaj masz odczytanie ile jest linii:
  1. <?php
  2. $file = "somefile.txt";
  3. $lines = count(file($file));
  4. echo "There are $lines lines in $file";
  5. ?>

Wtedy będziesz mógł puścic pętle od $lines-10 do $lines.
kiler129
Cytat(IceManSpy @ 22.09.2010, 00:19:29 ) *
Tutaj masz odczytanie ile jest linii:
  1. <?php
  2. $file = "somefile.txt";
  3. $lines = count(file($file));
  4. echo "There are $lines lines in $file";
  5. ?>

Wtedy będziesz mógł puścic pętle od $lines-10 do $lines.


Niestety nie wchodzi to w grę - file() ładuje cały plik do pamięci jako tablicę co z góry odpada przy zasobach jakie posiadam.
Udało mi się odgrzebać w swoich zbiorach taką funkcję a następnie lekko zmodyfikować:
  1. function unix_tail($f, $n) {
  2. $fp = fopen($f, "r");
  3. $pos = -2;
  4. $start = false;
  5. $ret = "";
  6. while($n > 0) {
  7. $t = " ";
  8. while($t != "\n") {
  9. if(fseek($fp, $pos, SEEK_END) == -1) {
  10. $start = true;
  11. break;
  12. }
  13. $t = fgetc($fp);
  14. $pos--;
  15. }
  16. $n--;
  17.  
  18. if($start) {
  19. rewind($fp);
  20. }
  21. $ret = $ret.fgets($fp);
  22. if($start) break;
  23. }
  24. fclose($fp);
  25. return $ret;
  26. }

Jednakże i ona ma swoje wady - te dwie pętle w sobie nie są zbyt wydajne. Ktoś na sali ma pomysł na optymalizację?
Pilsener
Musisz użyć fseek by poruszać się po pliku, już raz odpowiadałem na takie pytanie:
http://forum.php.pl/index.php?showtopic=13...st&p=698149
kiler129
Cytat(Pilsener @ 22.09.2010, 11:28:26 ) *
Musisz użyć fseek by poruszać się po pliku, już raz odpowiadałem na takie pytanie:
http://forum.php.pl/index.php?showtopic=13...st&p=698149

Podane przez ciebie rozwiązanie ma sens (i je znam) ale ma jednego buga - co jeśli linia ma zmienna długość od 10 do 400B? ;]
Przekopałem już sporo kodu w internecie i chyba tylko to co udało mi się znaleźć w swoich zbiorach działa w każdych warunkach (szukając \n).

edit:
W manie dokopałem się też do innego sposobu realizacji. Ma ograniczenie do 4KB/linię (chyba że się mylę w interpretacji kodu to proszę mnie poprawić). Jest nieco szybsza (~18%) od mojej.
Delikatnie zmodyfikowałem aby nie pluło tyle notice oraz domyślną ilość linii dla zgodności z *nixowym tailem (10 zamiast 100) winksmiley.jpg

  1. function unix_tail($file, $numLines = 10)
  2. {
  3. $fp = fopen($file, "r");
  4. $chunk = 4096;
  5. $data = "";
  6. $fs = sprintf("%u", filesize($file));
  7. $max = (intval($fs) == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file);
  8.  
  9. for ($len = 0; $len < $max; $len += $chunk) {
  10. $seekSize = ($max - $len > $chunk) ? $chunk : $max - $len;
  11.  
  12. fseek($fp, ($len + $seekSize) * -1, SEEK_END);
  13. $data = fread($fp, $seekSize) . $data;
  14.  
  15. if (substr_count($data, "\n") >= $numLines + 1) {
  16. preg_match("!(.*?\n){".($numLines)."}$!", $data, $match);
  17. fclose($fp);
  18. return $match[0];
  19. }
  20. }
  21. fclose($fp);
  22. return $data;
  23. }



edit2:
Pospieszylem się ... moja funckcja zawiera błąd powodujący odwrócenie kolejności linii.

edit3:
Jak widać zagadnienie taila w php nie jest takie banalne jak mogło by się wydawać. Po testach pokazana wyżej funkcja robi błędy!
Nie starałem się jej debugować jednakże czasami zwraca 3 linie zamiast 4, czasami pusty string (wtf?!).
Poprawiłem nieco moją funkcję dodając zmieniając return $ret na implode("\n", array_reverse(explode("\n", $ret))) [tak na szybko] i działa względnie poprawnie ... chociaż też nie do końca.

Czy ktokolwiek jest w stanie pokazać dlaczego ni jedna ni druga funkcja nie działają w 100% poprawnie? Ew. czy ktoś zna C i może powiedzieć jak robi to oryginalny tail?

edit4:
Udało mi się znaleźć funkcję która działa i jest 3x szybsza od mojej.
Naśladuje praktycznie w 100% taila.
  1. function unix_tail($file, $num_to_get=10)
  2. {
  3. $fp = fopen($file, 'r');
  4. $position = filesize($file);
  5. $chunklen = 4096;
  6. if($position-$chunklen <= 0 )fseek($fp,0);
  7. else fseek($fp, $position-$chunklen);
  8. $data="";$ret="";$lc=0;
  9. while($chunklen > 0)
  10. {
  11. $data = fread($fp, $chunklen);
  12. $dl=strlen($data);
  13. for($i=$dl-1;$i>=0;$i--){
  14. if($data[$i]=="\n"){
  15. if($lc==0 && $ret!="")$lc++;
  16. $lc++;
  17. if($lc>$num_to_get)return $ret;
  18. }
  19. $ret=$data[$i].$ret;
  20. }
  21. if($position-$chunklen <= 0 ){
  22. fseek($fp,0);
  23. $chunklen=$chunklen-abs($position-$chunklen);
  24. }else fseek($fp, $position-$chunklen);
  25. $position = $position - $chunklen;
  26. }
  27. fclose($fp);
  28. return $ret;
  29. }
Pilsener
Cytat
Podane przez ciebie rozwiązanie ma sens (i je znam) ale ma jednego buga - co jeśli linia ma zmienna długość od 10 do 400B? ;]
- to oczywiste, że linia ma zmienną długość dlatego należy założyć 10*maksymalna długość linii i od tego miejsca parsować plik przechowując w tablicy tylko 10 linijek. Identycznie mogłeś zrobić lekko modyfikując swój kod z początku tematu, ale po co przekopywać się przez cały plik? Jeśli ma parę mega to trochę by to trwało smile.gif
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.