Вот ответ, подобный MizardX, но без очевидной проблемы, заключающейся в том, чтобы в наихудшем случае взять квадратичное время от повторного сканирования рабочей строки для новых строк при добавлении кусков.
По сравнению с решением activestate (которое также кажется квадратичным), оно не взрывается при пустом файле и выполняет один поиск на чтение блока вместо двух.
По сравнению с нерестом "хвост", он самодостаточен. (Но «хвост» лучше, если он у вас есть.)
По сравнению с захватом нескольких килобайт с конца и в надежде, что этого достаточно, это работает для любой длины строки.
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)
Для использования по запросу:
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
Редактировать: изменил карту () на itertools.imap () в head (). Редактировать 2: упрощенный reversed_blocks (). Редактировать 3: избегать повторного сканирования хвоста для новых строк. Редактировать 4: переписал reversed_lines (), потому что str.splitlines () игнорирует финальный '\ n', как заметил BrianB (спасибо).
Обратите внимание, что в очень старых версиях Python конкатенация строк в цикле занимает квадратичное время. CPython, по крайней мере в последние несколько лет, автоматически решает эту проблему.