Piszę przeglądarkę plików dziennika dla aplikacji sieci web i do tego chcę paginować według wierszy pliku dziennika. Elementy w pliku są oparte na linii z najnowszym elementem na dole.
Potrzebuję więc tail()
metody, która może odczytywać n
linie od dołu i obsługuje przesunięcie. To, co wymyśliłem, wygląda następująco:
def tail(f, n, offset=0):
"""Reads a n lines from f with an offset of offset lines."""
avg_line_length = 74
to_read = n + offset
while 1:
try:
f.seek(-(avg_line_length * to_read), 2)
except IOError:
# woops. apparently file is smaller than what we want
# to step back, go to the beginning instead
f.seek(0)
pos = f.tell()
lines = f.read().splitlines()
if len(lines) >= to_read or pos == 0:
return lines[-to_read:offset and -offset or None]
avg_line_length *= 1.3
Czy to rozsądne podejście? Jaki jest zalecany sposób łączenia plików dziennika z przesunięciami?
seek(0,2)
wtedytell()
), i użyj tej wartości, aby wyszukać względem początku.open
polecenia wykorzystywanego do generowaniaf
obiektu plik powinien być określony, ponieważ zależnie od tego czyf=open(..., 'rb')
lub musi być przetwarzany różnief=open(..., 'rt')
f
Odpowiedzi:
To może być szybsze niż twoje. Nie przyjmuje żadnych założeń dotyczących długości linii. Cofa plik po bloku, aż znajdzie odpowiednią liczbę znaków „\ n”.
Nie lubię podchwytliwych założeń dotyczących długości linii, gdy - ze względów praktycznych - nigdy nie możesz wiedzieć takich rzeczy.
Zasadniczo spowoduje to zlokalizowanie ostatnich 20 linii przy pierwszym lub drugim przejściu przez pętlę. Jeśli twoja 74 postać jest właściwie dokładna, ustawiasz rozmiar bloku 2048 i prawie natychmiast odsuwasz 20 linii.
Ponadto nie spalam zbyt wiele kalorii w mózgu, próbując precyzyjnie dopasować się do fizycznych bloków systemu operacyjnego. Korzystając z tych wysokopoziomowych pakietów we / wy, wątpię, abyś zobaczył jakąkolwiek konsekwencję wydajnościową próbowania wyrównania na granicach bloków systemu operacyjnego. Jeśli używasz we / wy niższego poziomu, możesz zauważyć przyspieszenie.
AKTUALIZACJA
w Pythonie 3.2 i nowszych postępuj zgodnie z procesem bajtów, ponieważ w plikach tekstowych (otwartych bez „b” w ciągu trybu) dozwolone są tylko wyszukiwania względem początku pliku (wyjątek dotyczy samego końca pliku z funkcją seek (0, 2)) .:
na przykład:
f = open('C:/.../../apache_logs.txt', 'rb')
źródło
io.UnsupportedOperation: can't do nonzero end-relative seeks
że mogę zmienić offset na 0, ale to przeczy celowi funkcji.Zakłada system uniksopodobny w Pythonie 2, który możesz wykonać:
W przypadku Pythona 3 możesz wykonać:
źródło
offset_total = str(n+offset)
i zastąp tę linię,stdin,stdout = os.popen2("tail -n "+offset_total+" "+f)
aby uniknąćTypeErrors (cannot concatenate int+str)
Oto moja odpowiedź. Czysty python. Korzystanie z czasu wydaje się dość szybkie. Dostosowywanie 100 linii pliku dziennika zawierającego 100 000 linii:
Oto kod:
źródło
if len(lines_found) > lines:
naprawdę konieczne? Czyloop
warunek też tego nie złapie?os.SEEK_END
używane po prostu dla jasności? O ile się przekonałem, jego wartość jest stała (= 2). Zastanawiałem się, czy pominąć to, aby móc pominąćimport os
. Dzięki za świetne rozwiązanie!os.SEEK_END
liczbą całkowitą. Było tam głównie dla czytelności.while len(lines_found) < lines
sięwhile len(lines_found) <= lines
w moim egzemplarzu. Dzięki!Jeśli odczytanie całego pliku jest dopuszczalne, użyj deque.
Przed wersją 2.6 deques nie miały opcji maxlen, ale można ją łatwo wdrożyć.
Jeśli wymagane jest odczytanie pliku od końca, użyj wyszukiwania galopowego (aka wykładniczego).
źródło
pos *= 2
wydaje się całkowicie arbitralny. Jakie jest jego znaczenie?Powyższa odpowiedź S.Lott prawie dla mnie działa, ale ostatecznie daje mi częściowe zdanie. Okazuje się, że powoduje uszkodzenie danych na granicach bloków, ponieważ dane blokują odczytane bloki w odwrotnej kolejności. Po wywołaniu „.join (dane) bloki są w niewłaściwej kolejności. To naprawia to.
źródło
Kod, którego używałem. Myślę, że jak dotąd jest to najlepsze:
źródło
Proste i szybkie rozwiązanie z mmap:
źródło
.rfind
metody skanowania wstecz w poszukiwaniu nowych linii, zamiast sprawdzania bajtów naraz na poziomie Pythona; w CPython zastępowanie kodu poziomu Python Wbudowane połączenia C zwykle dużo wygrywają). W przypadku mniejszych wejść,deque
zmaxlen
jest prostsze i prawdopodobnie podobnie szybkie.Jeszcze czystsza wersja zgodna z Python3, która nie wstawia, ale dołącza i odwraca:
użyj tego w ten sposób:
źródło
Zaktualizuj rozwiązanie @papercrane do python3. Otwórz plik za pomocą
open(filename, 'rb')
i:źródło
Publikując odpowiedź na żądanie komentujących moją odpowiedź na podobne pytanie, w którym zastosowano tę samą technikę do mutacji ostatniego wiersza pliku, a nie tylko go otrzymuję.
W przypadku pliku o znacznych rozmiarach
mmap
jest to najlepszy sposób na zrobienie tego. Aby poprawić istniejącąmmap
odpowiedź, ta wersja jest przenośna między systemami Windows i Linux i powinna działać szybciej (chociaż nie będzie działać bez pewnych modyfikacji 32-bitowego Pythona z plikami w zakresie GB, zobacz inną odpowiedź na wskazówki dotyczące obsługi tego oraz do modyfikacji w celu pracy z Python 2 ).Zakłada się, że liczba linii jest na tyle mała, że można bezpiecznie odczytać je wszystkie do pamięci jednocześnie; możesz także ustawić tę funkcję jako generator i ręcznie odczytywać wiersz, zastępując ostatni wiersz:
Na koniec odczytany w trybie binarnym (konieczny do użycia
mmap
), więc podajestr
linie (Py2) ibytes
linie (Py3); jeśli chceszunicode
(Py2) lubstr
(Py3), iteracyjne podejście można zmodyfikować w celu odkodowania dla Ciebie i / lub naprawienia nowych linii:Uwaga: wpisałem to wszystko na maszynie, na której nie mam dostępu do Pythona do przetestowania. Daj mi znać, jeśli coś napisałem na maszynie; było to na tyle podobne do mojej innej odpowiedzi , że myślę, że powinno działać, ale poprawki (np. obsługa an
offset
) mogą prowadzić do subtelnych błędów. Daj mi znać w komentarzach, jeśli są jakieś błędy.źródło
Uważam, że Popen powyżej jest najlepszym rozwiązaniem. Jest szybki i brudny i działa W przypadku Pythona 2.6 na maszynie Unix zastosowałem następujące
Soutput będzie zawierał ostatnie n wierszy kodu. aby wykonać iterację przez Soutput linia po linii:
źródło
na podstawie najczęściej głosowanej odpowiedzi S.Lott (25 września 08 21:43), ale naprawiono dla małych plików.
Mam nadzieję, że to się przyda.
źródło
Istnieje kilka istniejących implementacji taila na pypi, które można zainstalować za pomocą pip:
W zależności od sytuacji korzystanie z jednego z tych istniejących narzędzi może być korzystne.
źródło
tailhead
,tailer
ale nie działały. Próbowałem teżmtFileUtil
. Początkowo powodował błąd, ponieważprint
instrukcje nie miały nawiasów (korzystam z Pythona 3.6). Dodałem jereverse.py
i komunikaty o błędach zniknęły, ale kiedy mój skrypt wywołuje moduł (mtFileUtil.tail(open(logfile_path), 5)
), nic nie drukuje.Prosty :
źródło
Aby uzyskać wydajność w przypadku bardzo dużych plików (często w sytuacjach, gdy konieczne jest użycie pliku dziennika), zwykle należy unikać czytania całego pliku (nawet jeśli robisz to bez wczytywania całego pliku do pamięci). trzeba jakoś wypracować przesunięcie w wierszach, a nie w znakach. Jedną z możliwości jest czytanie wstecz przy pomocy seek () char po char, ale jest to bardzo wolne. Zamiast tego lepiej jest przetwarzać w większych blokach.
Mam funkcję narzędzia, którą napisałem jakiś czas temu, aby odczytać pliki do tyłu, których można tu użyć.
[Edytuj] Dodano bardziej szczegółową wersję (pozwala uniknąć dwukrotnego cofnięcia)
źródło
możesz przejść do końca pliku za pomocą f.seek (0, 2), a następnie odczytywać wiersze jeden po drugim z następującym zamiennikiem readline ():
źródło
Na podstawie odpowiedzi Eyecue (10 czerwca 10 o 21:28): ta klasa dodaje metodę head () i tail () do obiektu pliku.
Stosowanie:
źródło
Kilka z tych rozwiązań ma problemy, jeśli plik nie kończy się na \ n lub zapewnia, że zostanie przeczytany cały pierwszy wiersz.
źródło
Oto całkiem prosta implementacja:
źródło
f.seek
? Dlaczego nie przedwith open
? Ponadto, dlaczegoexcept
robiszf.readlines()
??Istnieje bardzo przydatny moduł, który może to zrobić:
źródło
Inne rozwiązanie
jeśli twój plik txt wygląda tak: mysz wąż kot jaszczurka wilk pies
możesz odwrócić ten plik, po prostu używając indeksowania tablic w pythonie ''
wynik: pies wilk jaszczurka kot
źródło
Najprostszym sposobem jest użycie
deque
:źródło
Musiałem odczytać określoną wartość z ostatniego wiersza pliku i natknąłem się na ten wątek. Zamiast na nowo wymyślić koło w Pythonie, skończyłem z małym skryptem powłoki, zapisanym jako / usr / local / bin / get_last_netp:
A w programie Python:
źródło
Nie pierwszy przykład z użyciem deque, ale prostszy. Ten jest ogólny: działa na każdym iterowalnym obiekcie, nie tylko na pliku.
źródło
źródło
źródło
źródło
źródło
Aktualizacja dla odpowiedzi udzielonej przez A.Coady
Działa z python 3 .
Korzysta z wyszukiwania wykładniczego i buforuje tylko
N
wiersze z tyłu i jest bardzo wydajny.źródło
Po namyśle jest to prawdopodobnie tak szybkie, jak cokolwiek tutaj.
To o wiele prostsze. I wydaje się, że rozpruwa się w dobrym tempie.
źródło