Самый эффективный способ поиска последних x строк файла в python

У меня есть файл, и я не знаю, насколько он будет большой (он может быть довольно большим, но размер будет сильно отличаться). Я хочу найти последние 10 строк или около того, чтобы увидеть, соответствует ли какая-либо из них строка. Мне нужно сделать это как можно быстрее и эффективнее, и мне было интересно, есть ли что-то лучше, чем:

s = "foo" last_bit = fileObj.readlines()[-10:] for line in last_bit: if line == s: print "FOUND" 

  • os.path.getsize Возвращает неверное значение?
  • Каков наилучший способ открыть файл для эксклюзивного доступа в Python?
  • Извлечение расширения из имени файла в Python
  • Что означает «wb» в этом коде, используя Python?
  • Импортировать переменные из другого файла?
  • Web2py: загрузка файлов / отображение изображений
  • Перезаписывание ранее извлеченных файлов вместо создания новых
  • Строка Python как аргумент файла для подпроцесса
  • 16 Solutions collect form web for “Самый эффективный способ поиска последних x строк файла в python”

     # 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 

    Вот такой ответ, как MizardX, но без его очевидной проблемы взять квадратичное время в худшем случае от повторного сканирования рабочей строки для строк новой строки по мере добавления кусков.

    По сравнению с решением activestate (которое также представляется квадратичным), это не взрывается с учетом пустого файла, и каждый ищет в блоке вместо двух.

    По сравнению с нерестом «хвост» это самодостаточно. (Но «хвост» лучше, если он у вас есть.)

    По сравнению с захватом нескольких kB с конца и надеясь, что этого достаточно, это работает для любой длины линии.

     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: упростить reverseedblocks (). Изменить 3: избегать повторного сканирования хвоста для строк новой строки. Изменить 4: переписать reverseed_lines (), потому что str.splitlines () игнорирует окончательный '\ n', как заметил Брайан (спасибо).

    Обратите внимание, что в очень старых версиях Python конкатенация строк в цикле здесь займет квадратичное время. CPython, по крайней мере, в последние несколько лет, автоматически устраняет эту проблему.

    Если вы используете Python в системе POSIX, вы можете использовать «tail -10» для извлечения последних нескольких строк. Это может быть быстрее, чем писать собственный код Python, чтобы получить последние 10 строк. Вместо того, чтобы открывать файл напрямую, откройте канал из команды «tail -10 filename». Если вы уверены в выходе журнала (например, вы знаете, что никогда не бывает очень длинных строк длиной до сотни или тысячи символов), то использование одного из перечисленных «двух последних двух КБ» подходов будет хорошо.

    Я думаю, что чтение последних 2 КБ или около того файла должно гарантировать, что вы получите 10 строк, и не должно быть слишком много ресурсов.

     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) 

    Вот версия с использованием mmap которая кажется довольно эффективной. Большим плюсом является то, что mmap автоматически будет обрабатывать требования к пейджинговому файлу для вас.

     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] 

    Я думаю, что я помню, как адаптировать код из этого сообщения в блоге от Manu Garg, когда мне приходилось делать что-то подобное.

    Если вы находитесь в окне unix, os.popen("tail -10 " + filepath).readlines() , вероятно, будет самым быстрым способом. В противном случае это зависит от того, насколько вы надежны. Предлагаемые до сих пор методы все равно будут падать, так или иначе. Для надежности и скорости в наиболее распространенном случае вы, вероятно, хотите что-то вроде логарифмического поиска: используйте file.seek для перехода к концу файла минус 1000 символов, прочитайте его, проверьте, сколько строк оно содержит, затем EOF минус 3000 символов , читать в 2000 символов, подсчитывать строки, затем EOF минус 7000, читать 4000 символов, подсчитывать строки и т. д., пока у вас не будет столько строк, сколько вам нужно. Но если вы точно знаете, что это всегда будет запускаться в файлах с разумной длиной линии, вам может и не понадобиться.

    Вы также можете найти вдохновение в исходном коде команды unix tail .

    Я столкнулся с этой проблемой, проанализировав последний час файлов Syslog LARGE и использовал эту функцию на сайте рецептов activestate … ( http://code.activestate.com/recipes/439045/ )

     !/usr/bin/env python # -*-mode: python; coding: iso-8859-1 -*- # # Copyright (c) Peter Astrand <astrand@cendio.se> import os import string class BackwardsReader: """Read a file line by line, backwards""" BLKSIZE = 4096 def readline(self): while 1: newline_pos = string.rfind(self.buf, "\n") pos = self.file.tell() if newline_pos != -1: # Found a newline line = self.buf[newline_pos+1:] self.buf = self.buf[:newline_pos] if pos != 0 or newline_pos != 0 or self.trailing_newline: line += "\n" return line else: if pos == 0: # Start-of-file return "" else: # Need to fill buffer toread = min(self.BLKSIZE, pos) self.file.seek(-toread, 1) self.buf = self.file.read(toread) + self.buf self.file.seek(-toread, 1) if pos - toread == 0: self.buf = "\n" + self.buf def __init__(self, file): self.file = file self.buf = "" self.file.seek(-1, 2) self.trailing_newline = 0 lastchar = self.file.read(1) if lastchar == "\n": self.trailing_newline = 1 self.file.seek(-1, 2) # Example usage br = BackwardsReader(open('bar')) while 1: line = br.readline() if not line: break print repr(line) 

    Он работает очень хорошо и намного эффективнее, чем что-то вроде fileObj.readlines () [- 10:], что заставляет python читать весь файл в памяти, а затем отрубает последние десять строк.

    Вы можете читать фрагменты размером 1000 байт или около того из конца файла в буфер до 10 строк.

    Вы также можете подсчитывать строки при обратном просмотре файла, а не гадать при смещении байта.

     lines = 0 chunk_size = 1024 f = file('filename') f.seek(0, 2) f.seek(f.tell() - chunk_size) while True: s = f.read(chunk_size) lines += s.count('\n') if lines > NUM_OF_LINES: break f.seek(f.tell() - chunk_size*2) 

    Теперь файл находится в хорошем положении для запуска readlines() . Вы также можете кэшировать строки, которые вы читаете в первый раз, чтобы дважды очистить одну и ту же часть файла.

    Я принял предложение mhawke использовать mmap и написал версию, использующую rfind :

     from mmap import mmap import sys def reverse_file(f): mm = mmap(f.fileno(), 0) nl = mm.size() - 1 prev_nl = mm.size() while nl > -1: nl = mm.rfind('\n', 0, nl) yield mm[nl + 1:prev_nl] prev_nl = nl + 1 def main(): # Example usage with open('test.txt', 'r+') as infile: for line in reverse_file(infile): sys.stdout.write(line) 

    прочитайте последние несколько Ks файла и разделите его на строки, чтобы вернуть только последние 10.

    маловероятно, чтобы начало этого куска падало на границу линии, но вы все равно отбросите первые строки.

    Лично у меня возникло бы желание выйти в оболочку и вызвать tail -n10 для загрузки файла. Но тогда я не программист на Python;)

    Во-первых, функция, которая возвращает список:

     def lastNLines(file, N=10, chunksize=1024): lines = None file.seek(0,2) # go to eof size = file.tell() for pos in xrange(chunksize,size-1,chunksize): # read a chunk file.seek(pos,2) chunk = file.read(chunksize) if lines is None: # first time lines = chunk.splitlines() else: # other times, update the 'first' line with # the new data, and re-split lines[0:1] = (chunk + lines[0]).splitlines() if len(lines) > N: return lines[-N:] file.seek(0) chunk = file.read(size-pos) lines[0:1] = (chunk + lines[0]).splitlines() return lines[-N:] 

    Во-вторых, функция, которая выполняет итерации по строкам в обратном порядке:

     def iter_lines_reversed(file, chunksize=1024): file.seek(0,2) size = file.tell() last_line = "" for pos in xrange(chunksize,size-1,chunksize): # read a chunk file.seek(pos,2) chunk = file.read(chunksize) + last_line # split into lines lines = chunk.splitlines() last_line = lines[0] # iterate in reverse order for index,line in enumerate(reversed(lines)): if index > 0: yield line # handle the remaining data at the beginning of the file file.seek(0) chunk = file.read(size-pos) + last_line lines = chunk.splitlines() for line in reversed(lines): yield line 

    Для вашего примера:

     s = "foo" for index, line in enumerate(iter_lines_reversed(fileObj)): if line == s: print "FOUND" break elif index+1 >= 10: break 

    Изменить: теперь автоматическое получение размера файла
    Edit2: теперь выполняется только итерация для 10 строк.

    Это решение будет читать файл только один раз, но с помощью 2 указателей объектов файла, чтобы получить последние N строк файла без повторного чтения:

     def getLastLines (path, n): # return the las N lines from the file indicated in path fp = open(path) for i in range(n): line = fp.readline() if line == '': return [] back = open(path) for each in fp: back.readline() result = [] for line in back: result.append(line[:-1]) return result s = "foo" last_bit = getLastLines(r'C:\Documents and Settings\ricardo.m.reyes\My Documents\desarrollo\tail.py', 10) for line in last_bit: if line == s: print "FOUND" 

    Возможно, это может быть полезно:

     import os.path path = 'path_to_file' os.system('tail -n1 ' + path) 
    Interesting Posts

    pip ничего не может установить

    Обтекание пользовательского типа указателем C ++ в Cython

    как проверить, является ли файл каталогом или обычным файлом в python?

    Как сделать элементы рисовать в то же время в python с помощью черепахи?

    Как изменить папку загрузки по умолчанию во время работы webdriver?

    Python regex \ w не соответствует сочетанию диакритики?

    модуль 'не имеет атрибута' drawMatches 'opencv python

    вызывающий func. изменить вход

    `pip install pandas` дает UnicodeDecodeError: кодек ascii не может декодировать байт 0xe2 в позиции 41: порядковый номер не в диапазоне (128)

    Разница между np.random.seed () и np.random.RandomState ()

    Каков самый быстрый способ найти собственные значения / векторы в python?

    В Python, как мне декодировать кодировку GZIP?

    Определение портов прослушивания с использованием Python

    Как развернуть / перенести существующее приложение / проект django на производственный сервер на Heroku?

    Как сгенерировать уникальные 64-битные целые числа из Python?

    Python - лучший язык программирования в мире.