
Harley Holcombe
Ich habe eine Datei und weiß nicht, wie groß sie sein wird (sie könnte ziemlich groß sein, aber die Größe variiert stark). Ich möchte die letzten 10 Zeilen oder so durchsuchen, um zu sehen, ob eine davon mit einer Zeichenfolge übereinstimmt. Ich muss dies so schnell und effizient wie möglich tun und habe mich gefragt, ob es etwas Besseres gibt als:
s = "foo"
last_bit = fileObj.readlines()[-10:]
for line in last_bit:
if line == s:
print "FOUND"

PabloG
# Tail
from __future__ import with_statement
find_str = "FIREFOX" # String to find
fname = "g:/autoIt/ActiveWin.log_2" # File to check
with open(fname, "r") as f:
f.seek (0, 2) # Seek @ EOF
fsize = f.tell() # Get Size
f.seek (max (fsize-1024, 0), 0) # Set pos @ last n chars
lines = f.readlines() # Read to end
lines = lines[-10:] # Get last 10 lines
# This returns True if any line is exactly find_str + "n"
print find_str + "n" in lines
# If you're searching for a substring
for line in lines:
if find_str in line:
print True
break
Hier ist eine Antwort wie die von MizardX, jedoch ohne das offensichtliche Problem, dass im schlimmsten Fall quadratische Zeit benötigt wird, um den Arbeitsstring wiederholt nach Zeilenumbrüchen zu durchsuchen, wenn Chunks hinzugefügt werden.
Im Vergleich zur Active State-Lösung (die ebenfalls quadratisch zu sein scheint) explodiert dies bei einer leeren Datei nicht und sucht pro gelesenem Block statt zwei.
Im Vergleich zum laichenden ‘Schwanz’ ist dies in sich abgeschlossen. (Aber ‘Schwanz’ ist am besten, wenn Sie ihn haben.)
Im Vergleich dazu, ein paar kB vom Ende zu nehmen und zu hoffen, dass es genug ist, funktioniert dies für jede Zeilenlänge.
import os
def reversed_lines(file):
"Generate the lines of file in reverse order."
part=""
for block in reversed_blocks(file):
for c in reversed(block):
if c == 'n' and part:
yield part[::-1]
part=""
part += c
if part: yield part[::-1]
def reversed_blocks(file, blocksize=4096):
"Generate blocks of file's contents in reverse order."
file.seek(0, os.SEEK_END)
here = file.tell()
while 0 < here:
delta = min(blocksize, here)
here -= delta
file.seek(here, os.SEEK_SET)
yield file.read(delta)
Um es wie gewünscht zu verwenden:
from itertools import islice
def check_last_10_lines(file, key):
for line in islice(reversed_lines(file), 10):
if line.rstrip('n') == key:
print 'FOUND'
break
Bearbeiten: map() in itertools.imap() in head() geändert. Bearbeiten 2: vereinfachte reversed_blocks(). Bearbeiten 3: Vermeiden Sie das erneute Scannen des Schwanzes für Zeilenumbrüche. Bearbeiten 4: schrieb reversed_lines() um, weil str.splitlines() ein abschließendes ‘n’ ignoriert, wie BrianB bemerkte (danke).
Beachten Sie, dass in sehr alten Python-Versionen die String-Verkettung in einer Schleife hier quadratische Zeit in Anspruch nimmt. CPython aus den letzten Jahren vermeidet dieses Problem automatisch.
Wenn Sie Python auf einem POSIX-System ausführen, können Sie ‘tail -10’ verwenden, um die letzten paar Zeilen abzurufen. Dies kann schneller sein, als Ihren eigenen Python-Code zu schreiben, um die letzten 10 Zeilen zu erhalten. Anstatt die Datei direkt zu öffnen, öffnen Sie eine Pipe mit dem Befehl ‘tail -10 filename’. Wenn Sie sich jedoch der Protokollausgabe sicher sind (z. B. wissen Sie, dass es noch nie irgendwelche sehr langen Zeilen, die Hunderte oder Tausende von Zeichen lang sind), dann wäre es in Ordnung, einen der aufgeführten Ansätze zum Lesen der letzten 2 KB zu verwenden.

Ryan Ginstrom
Ich denke, das Lesen der letzten 2 KB der Datei sollte sicherstellen, dass Sie 10 Zeilen erhalten, und sollte nicht zu viel Ressourcenfresser sein.
file_handle = open("somefile")
file_size = file_handle.tell()
file_handle.seek(max(file_size - 2*1024, 0))
# this will get rid of trailing newlines, unlike readlines()
last_10 = file_handle.read().splitlines()[-10:]
assert len(last_10) == 10, "Only read %d lines" % len(last_10)
Hier ist eine Version mit mmap
das scheint ziemlich effizient zu sein. Das große Plus ist das mmap
verarbeitet automatisch die Auslagerungsanforderungen für die Datei zum Speicher für Sie.
import os
from mmap import mmap
def lastn(filename, n):
# open the file and mmap it
f = open(filename, 'r+')
m = mmap(f.fileno(), os.path.getsize(f.name))
nlcount = 0
i = m.size() - 1
if m[i] == 'n': n += 1
while nlcount < n and i > 0:
if m[i] == 'n': nlcount += 1
i -= 1
if i > 0: i += 2
return m[i:].splitlines()
target = "target string"
print [l for l in lastn('somefile', 10) if l == target]

Daryl Spitzer
Ich glaube, ich erinnere mich, den Code von . angepasst zu haben dieser Blogbeitrag von Manu Garg als ich etwas ähnliches machen musste.

Alex Coventry
Wenn Sie eine Unix-Box verwenden, os.popen("tail -10 " + filepath).readlines()
wird wohl der schnellste Weg sein. Ansonsten hängt es davon ab, wie robust Sie es haben möchten. Die bisher vorgeschlagenen Methoden werden alle auf die eine oder andere Weise scheitern. Für Robustheit und Geschwindigkeit möchten Sie im häufigsten Fall wahrscheinlich so etwas wie eine logarithmische Suche: Verwenden Sie file.seek, um zum Ende der Datei minus 1000 Zeichen zu gehen, sie einzulesen, zu überprüfen, wie viele Zeilen sie enthält, dann zu EOF minus 3000 Zeichen , 2000 Zeichen einlesen, Zeilen zählen, dann EOF minus 7000, 4000 Zeichen einlesen, Zeilen zählen usw. bis Sie so viele Zeilen haben, wie Sie brauchen. Aber wenn Sie sicher sind, dass es immer auf Dateien mit vernünftigen Zeilenlängen ausgeführt wird, brauchen Sie das möglicherweise nicht.
Vielleicht finden Sie auch Inspiration in der Quellcode für das Unix tail
Befehl.
.
4603600cookie-checkDie effizienteste Methode, um die letzten X Zeilen einer Datei zu durchsuchen?yes
Exaktes Duplikat von stackoverflow.com/questions/136168/tail-a-file-with-python.
– S.Lott
4. November ’08 um 2:05