Это идеальный пример использования ограниченной длины collections.deque
:
from collections import deque
line_history = deque(maxlen=25)
with open(file) as input:
for line in input:
if "error code" in line:
print(*line_history, line, sep='')
# Clear history so if two errors seen in close proximity, we don't
# echo some lines twice
line_history.clear()
else:
# When deque reaches 25 lines, will automatically evict oldest
line_history.append(line)
Полное объяснение того, почему я выбрал этот подход (пропустите, если вам все равно):
Это не решается хорошим/безопасным способом с использованием for
/range
, потому что индексация имеет смысл только в том случае, если вы загружаете весь файл в память; файл на диске не знает, где строки начинаются и заканчиваются, поэтому вы не можете просто запросить «строку № 357 файла», не читая ее с самого начала, чтобы найти строки с 1 по 356. Вы либо в конечном итоге неоднократно перечитываете файл или поместить весь файл в последовательность в памяти (например, list
/tuple
), чтобы индексация имела смысл.
Что касается файла журнала, вы должны предположить, что он может быть довольно большим (я регулярно имею дело с файлами журнала размером в несколько гигабайт), до такой степени, что загрузка его в память приведет к исчерпанию основной памяти, поэтому хлюпание — плохая идея, и повторное чтение файла с нуля каждый раз, когда вы сталкиваетесь с ошибкой, почти так же плохо (это медленно, но надежно медленно, я думаю?). Подход, основанный на deque
, означает, что ваше пиковое использование памяти основано на 27 самых длинных строках в файле, а не на общем размере файла.
Наивное решение, в котором нет ничего, кроме встроенных функций, может быть таким простым, как:
with open(file) as input:
lines = tuple(input) # Slurps all lines from file
for i, line in enumerate(lines):
if "error code" in line:
print(*lines[max(i-25, 0):i], line, sep='')
но, как я уже сказал, для этого требуется достаточно памяти, чтобы одновременно хранить в памяти весь файл журнала, на что рассчитывать не стоит. Он также повторяет строки, когда две ошибки происходят в непосредственной близости, потому что, в отличие от deque
, у вас нет простого способа очистить вашу недавнюю память; вам придется вручную отслеживать индекс последних print
, чтобы ограничить свой фрагмент.
Обратите внимание, что даже тогда я не использовал range
; range
— это костыль, на который полагаются многие люди, пришедшие из C, но обычно это неправильный способ решения проблемы в Python. В тех случаях, когда индекс необходим (обычно это не так), обычно требуется и значение, поэтому решения на основе enumerate
предпочтительнее; в большинстве случаев вам вообще не нужен индекс, поэтому правильным решением является прямая итерация (или парная итерация с zip
и т.п.).
person
ShadowRanger
schedule
24.08.2018