Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> [PLIKI] Odczytanie X ostatnich linii pliku
kiler129
post
Post #1





Grupa: Zarejestrowani
Postów: 566
Pomógł: 35
Dołączył: 21.06.2006

Ostrzeżenie: (0%)
-----


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ą (IMG:style_emoticons/default/dry.gif)


Czy któryś z kolegów może podać mi sposób na implementację funkcji tail w php?
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi (1 - 5)
IceManSpy
post
Post #2





Grupa: Zarejestrowani
Postów: 1 006
Pomógł: 111
Dołączył: 23.07.2010
Skąd: Kraków

Ostrzeżenie: (0%)
-----


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.
Go to the top of the page
+Quote Post
kiler129
post
Post #3





Grupa: Zarejestrowani
Postów: 566
Pomógł: 35
Dołączył: 21.06.2006

Ostrzeżenie: (0%)
-----


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ę?
Go to the top of the page
+Quote Post
Pilsener
post
Post #4





Grupa: Zarejestrowani
Postów: 1 590
Pomógł: 185
Dołączył: 19.04.2006
Skąd: Gdańsk

Ostrzeżenie: (0%)
-----


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
Go to the top of the page
+Quote Post
kiler129
post
Post #5





Grupa: Zarejestrowani
Postów: 566
Pomógł: 35
Dołączył: 21.06.2006

Ostrzeżenie: (0%)
-----


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) (IMG:style_emoticons/default/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. }


Ten post edytował kiler129 22.09.2010, 18:48:52
Go to the top of the page
+Quote Post
Pilsener
post
Post #6





Grupa: Zarejestrowani
Postów: 1 590
Pomógł: 185
Dołączył: 19.04.2006
Skąd: Gdańsk

Ostrzeżenie: (0%)
-----


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 (IMG:style_emoticons/default/smile.gif)
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 24.08.2025 - 07:56