Вывести количество предыдущих строк после совпадения строки, найденной в строке в python - PullRequest
0 голосов
/ 24 августа 2018

Я пишу программу для анализа некоторых файлов журнала. Если в строке указан код ошибки, мне нужно распечатать предыдущие 25 строк для анализа. Я хотел бы иметь возможность повторить эту концепцию с более или менее строк в зависимости от кода отдельной ошибки (вместо 25 строк, 15 или 35).

with open(file, 'r') as input:
     for line in input:
         if "error code" in line: 
             #print previous 25 lines

Мне известна эквивалентная команда в Bash для grep "error code" -B 25 Filename | wc -1. Я все еще новичок в Python и программировании в целом, я знаю, что мне понадобится цикл for, и я попытался использовать функцию range, но мне не повезло, потому что я не не знаю, как реализовать диапазон в файлах.

Ответы [ 2 ]

0 голосов
/ 24 августа 2018

Попробуйте базовое кодирование с циклом for и функцией range без каких-либо специальных библиотек:

N = 25
with open(file, 'r') as f:
    lines = f.read().splitlines()
    for i, line in enumerate(lines):
        if "error code" in line: 
            j = i-N if i>N else 0
            for k in range(j,i):
                print(lines[k])

Выше печатает предыдущие 25 строк или с первой строки, если общее количество строк меньше 25.

Кроме того, лучше избегать использования input в качестве переменного термина, поскольку это ключевое слово в Python.

0 голосов
/ 24 августа 2018

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

...