Ich schreibe einen Protokolldatei-Viewer für eine Webanwendung und möchte dafür durch die Zeilen der Protokolldatei paginieren. Die Elemente in der Datei basieren auf dem neuesten Element unten.
Ich brauche also eine tail()
Methode, die n
Zeilen von unten lesen kann und einen Versatz unterstützt. Was ich mir ausgedacht habe, sieht so aus:
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
Ist das ein vernünftiger Ansatz? Was ist die empfohlene Methode, um Protokolldateien mit Offsets zu versehen?
seek(0,2)
danntell()
) zu erhalten, und verwende diesen Wert, um relativ zum Anfang zu suchen.open
Befehls, der zum Generieren desf
f=open(..., 'rb')
f=open(..., 'rt')
f
Antworten:
Dies kann schneller sein als deins. Macht keine Annahmen über die Leitungslänge. Durchläuft die Datei blockweise, bis die richtige Anzahl von '\ n' Zeichen gefunden wurde.
Ich mag keine kniffligen Annahmen über die Zeilenlänge, wenn man so etwas praktisch nie wissen kann.
Im Allgemeinen werden dadurch die letzten 20 Zeilen beim ersten oder zweiten Durchgang durch die Schleife lokalisiert. Wenn Ihre Sache mit 74 Zeichen tatsächlich korrekt ist, stellen Sie die Blockgröße 2048 ein und Sie werden fast sofort 20 Zeilen abschließen.
Außerdem verbrenne ich nicht viele Gehirnkalorien, um die Ausrichtung auf physische Betriebssystemblöcke zu verfeinern. Ich bezweifle, dass Sie bei Verwendung dieser übergeordneten E / A-Pakete Leistungsfolgen sehen, wenn Sie versuchen, sich an den Blockgrenzen des Betriebssystems auszurichten. Wenn Sie E / A auf niedrigerer Ebene verwenden, wird möglicherweise eine Beschleunigung angezeigt.
AKTUALISIEREN
Befolgen Sie für Python 3.2 und höher den Vorgang für Bytes wie in Textdateien (diejenigen, die ohne ein "b" in der Moduszeichenfolge geöffnet wurden ), sind nur Suchvorgänge relativ zum Dateianfang zulässig (mit Ausnahme der Suche bis zum Ende der Datei) mit suchen (0, 2)).:
z.B:
f = open('C:/.../../apache_logs.txt', 'rb')
quelle
io.UnsupportedOperation: can't do nonzero end-relative seeks
ich kann den Offset auf 0 ändern, aber das macht den Zweck der Funktion zunichte.Nimmt ein Unix-ähnliches System unter Python 2 an, das Sie ausführen können:
Für Python 3 können Sie Folgendes tun:
quelle
offset_total = str(n+offset)
folgt vorberechnen und diese Zeile ersetzenstdin,stdout = os.popen2("tail -n "+offset_total+" "+f)
, um zu vermeidenTypeErrors (cannot concatenate int+str)
Hier ist meine Antwort. Reine Python. Mit timeit scheint es ziemlich schnell zu sein. Tailing 100 Zeilen einer Protokolldatei mit 100.000 Zeilen:
Hier ist der Code:
quelle
if len(lines_found) > lines:
wirklich notwendig? Würde derloop
Zustand es nicht auch fangen?os.SEEK_END
Wird nur zur Klarheit verwendet? Soweit ich festgestellt habe, ist sein Wert konstant (= 2). Ich habe mich gefragt, ob ich es weglassen soll, um das weglassen zu könnenimport os
. Danke für die tolle Lösung!os.SEEK_END
das Integer-Äquivalent ersetzen . Es war hauptsächlich für die Lesbarkeit da.while len(lines_found) < lines
umwhile len(lines_found) <= lines
in meinem Exemplar. Vielen Dank!Wenn das Lesen der gesamten Datei akzeptabel ist, verwenden Sie eine Deque.
Vor 2.6 hatten Deques keine Maxlen-Option, aber die Implementierung ist einfach genug.
Wenn es erforderlich ist, die Datei vom Ende an zu lesen, verwenden Sie eine Galoppsuche (auch Exponentialsuche genannt).
quelle
pos *= 2
scheint völlig willkürlich. Welche Bedeutung hat es?Die Antwort von S.Lott oben funktioniert fast für mich, gibt mir aber am Ende Teilzeilen. Es stellt sich heraus, dass Daten an Blockgrenzen beschädigt werden, da die Daten die Leseblöcke in umgekehrter Reihenfolge enthalten. Wenn '' .join (Daten) aufgerufen wird, sind die Blöcke in der falschen Reihenfolge. Dies behebt das.
quelle
Der Code, den ich letztendlich verwendet habe. Ich denke, das ist das bisher beste:
quelle
Einfache und schnelle Lösung mit mmap:
quelle
.rfind
Methode verwendet würde, um rückwärts nach Zeilenumbrüchen zu suchen, anstatt Byte-zu-Zeit-Überprüfungen auf Python-Ebene durchzuführen; in CPython wird Python-Code durch Code ersetzt C eingebaute Anrufe gewinnen normalerweise um ein Vielfaches). Für kleinere Eingänge ist dasdeque
mit amaxlen
einfacher und wahrscheinlich ähnlich schnell.Eine noch sauberere Python3-kompatible Version, die nicht einfügt, sondern anfügt und umkehrt:
benutze es so:
quelle
Aktualisieren Sie die @ papercrane-Lösung auf python3. Öffnen Sie die Datei mit
open(filename, 'rb')
und:quelle
Eine Antwort auf Geheiß von Kommentatoren zu meiner Antwort auf eine ähnliche Frage posten bei der dieselbe Technik verwendet wurde, um die letzte Zeile einer Datei zu mutieren, nicht nur um sie zu erhalten.
mmap
Dies ist für eine Datei von erheblicher Größe der beste Weg, dies zu tun. Um die vorhandenemmap
Antwort zu verbessern , ist diese Version zwischen Windows und Linux portierbar und sollte schneller ausgeführt werden (obwohl sie ohne einige Änderungen an 32-Bit-Python mit Dateien im GB-Bereich nicht funktioniert. Hinweise zur Handhabung finden Sie in der anderen Antwort und zum Ändern für Python 2 ).Dies setzt voraus, dass die Anzahl der getailten Zeilen klein genug ist, damit Sie sie alle sicher gleichzeitig in den Speicher einlesen können. Sie können dies auch zu einer Generatorfunktion machen und jeweils eine Zeile manuell lesen, indem Sie die letzte Zeile durch Folgendes ersetzen:
Zuletzt wird dies im Binärmodus gelesen (zur Verwendung erforderlich
mmap
), sodassstr
Zeilen (Py2) undbytes
Zeilen (Py3) erhalten werden. Wenn Sieunicode
(Py2) oderstr
(Py3) möchten, kann der iterative Ansatz optimiert werden, um für Sie zu dekodieren und / oder Zeilenumbrüche zu korrigieren:Hinweis: Ich habe dies alles auf einem Computer eingegeben, auf dem ich keinen Zugriff auf Python zum Testen habe. Bitte lassen Sie mich wissen, wenn ich etwas getippt habe. Dies war meiner anderen Antwort so ähnlich, dass ich denke, es sollte funktionieren, aber die Optimierungen (z. B. Handhabung eines
offset
) könnten zu subtilen Fehlern führen. Bitte lassen Sie mich in den Kommentaren wissen, wenn es Fehler gibt.quelle
Ich fand den Popen oben die beste Lösung. Es ist schnell und schmutzig und es funktioniert Für Python 2.6 auf Unix-Computer habe ich Folgendes verwendet
Soutput enthält die letzten n Zeilen des Codes. Zeile für Zeile durch Soutput iterieren:
quelle
basierend auf S.Lotts bester Antwort (25. September 2008 um 21:43 Uhr), aber behoben für kleine Dateien.
Hoffe das ist nützlich.
quelle
Es gibt einige vorhandene Implementierungen von tail on pypi, die Sie mit pip installieren können:
Abhängig von Ihrer Situation kann die Verwendung eines dieser vorhandenen Tools Vorteile haben.
quelle
tailhead
,tailer
aber sie haben nicht funktioniert. Auch versuchtmtFileUtil
. Es war anfangs ein Fehler, weilprint
Anweisungen keine Klammern hatten (ich bin auf Python 3.6). Ich habe diese hinzugefügtreverse.py
und die Fehlermeldungen waren verschwunden, aber wenn mein Skript das Modul (mtFileUtil.tail(open(logfile_path), 5)
) aufruft , wird nichts gedruckt.Einfach:
quelle
Aus Gründen der Effizienz bei sehr großen Dateien (häufig in Protokolldateisituationen, in denen Sie möglicherweise tail verwenden möchten) möchten Sie im Allgemeinen vermeiden, die gesamte Datei zu lesen (auch wenn Sie dies tun, ohne die gesamte Datei auf einmal in den Speicher einzulesen) müssen irgendwie den Versatz in Zeilen anstatt in Zeichen berechnen. Eine Möglichkeit besteht darin, mit seek () char by char rückwärts zu lesen, dies ist jedoch sehr langsam. Stattdessen ist es besser, in größeren Blöcken zu verarbeiten.
Ich habe eine Dienstprogrammfunktion, die ich vor einiger Zeit geschrieben habe, um Dateien rückwärts zu lesen, die hier verwendet werden können.
[Bearbeiten] Spezifischere Version hinzugefügt (vermeidet die Notwendigkeit, zweimal umzukehren)
quelle
Sie können mit f.seek (0, 2) zum Ende Ihrer Datei gehen und dann die Zeilen einzeln mit dem folgenden Ersatz für readline () ablesen:
quelle
Basierend auf der Eyecue-Antwort (10. Juni 10 um 21:28 Uhr): Diese Klasse fügt dem Dateiobjekt die Methoden head () und tail () hinzu.
Verwendung:
quelle
Einige dieser Lösungen haben Probleme, wenn die Datei nicht mit \ n endet oder die vollständige erste Zeile gelesen wird.
quelle
Hier ist eine ziemlich einfache Implementierung:
quelle
f.seek
? Warum nicht vor demwith open
? Auch warum in derexcept
du einf.readlines()
??Es gibt ein sehr nützliches Modul , das dies tun kann:
quelle
Eine andere Lösung
Wenn Ihre TXT-Datei so aussieht: Maus Schlange Katze Eidechse Wolf Hund
Sie können diese Datei umkehren, indem Sie einfach die Array-Indizierung in Python '' 'verwenden.
Ergebnis: Hund Wolf Eidechse Katze
quelle
Der einfachste Weg ist zu verwenden
deque
:quelle
Ich musste einen bestimmten Wert aus der letzten Zeile einer Datei lesen und bin auf diesen Thread gestoßen. Anstatt das Rad in Python neu zu erfinden, erhielt ich ein winziges Shell-Skript, das unter / usr / local / bin / get_last_netp gespeichert wurde:
Und im Python-Programm:
quelle
Nicht das erste Beispiel mit einer Deque, sondern ein einfacheres. Dieser ist allgemein: Er funktioniert mit jedem iterierbaren Objekt, nicht nur mit einer Datei.
quelle
quelle
quelle
quelle
quelle
Update für die Antwort von A.Coady
Funktioniert mit Python 3 .
Dies verwendet die exponentielle Suche und puffert nur
N
Zeilen von hinten und ist sehr effizient.quelle
Auf den zweiten Blick ist dies wahrscheinlich genauso schnell wie alles hier.
Es ist viel einfacher. Und es scheint in einem guten Tempo voranzukommen.
quelle