Это идеальный вариант использования для ограниченной длины 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
или подобным).