Home > Blockchain >  move backwards when iterate a for in a file
move backwards when iterate a for in a file

Time:06-08

imagine this situation:

a file with 1000 lines. the name of the file is file.txt

file = file.txt
word = 'error'
for line in file:
    if word in line:
        execute things

if I want the 8 lines BEFORE the line with the word "error", how I get it?

CodePudding user response:

Read the file and save the lines in a deque of a fixed size

from collections import deque

file = "file.txt"
word = 'error'

lines = deque(maxlen=8)
with open(file) as f:
    for line in f:
        if word in line:
            break
        lines.append(line)

print(lines)

CodePudding user response:

You can use combination of collections.deque() with fixed length and itertools.takewhile().

from collections import deque
from itertools import takewhile

with open("file.txt") as f:
    lines = deque(takewhile(lambda l: "error" not in l, f), maxlen=8)
print(*lines, sep="", end="")

You can help my country, check my profile info.

CodePudding user response:

For efficiency, read the lines in reverse order and when you encounter the error, start counting 8 lines and insert into a list eight_lines at position 0. Finally, you will have the eight lines in the eight_lines list in the correct order.

file = "file.txt"
word = 'error'

line_count = 0
eight_lines = []
for line in reversed(list(open(file))):
    if line_count > 0 and line_count < 9:
        eight_lines.insert(0, line)
        line_count  = 1
    if line_count > 8:
        break 
    if word in line:
        line_count  = 1

For people who comment on efficiency, I include the benchmark results for all three methods here. My solution is the fastest for this file size, because the whole file is read only once and the eight_lines list is also populated only once:

from timeit import timeit

print('Reverse:',   timeit(lambda:read_reverse(),number=1000),'ms')
print('Deque:',     timeit(lambda:read_deque(),number=1000),'ms')
print('Takewhile:', timeit(lambda:read_takewhile(),number=1000),'ms')

# Reverse:   0.1566204 ms
# Deque:     0.18476900000000002 ms
# Takewhile: 0.20178000000000001 ms

Here are the functions I benchmarked if someone wants to reproduce:

def read_reverse():
    file = "file.txt"
    word = 'error'
    
    line_count = 0
    eight_lines = []
    for line in reversed(list(open(file))):
        if line_count > 0 and line_count < 9:
            eight_lines.insert(0, line)
            line_count  = 1
        if line_count > 8:
            break 
        if word in line:
            line_count  = 1
    return eight_lines
    

from collections import deque
def read_deque():
    file = "file.txt"
    word = 'error'

    lines = deque(maxlen=8)
    with open(file) as f:
        for line in f:
            if word in line:
                break
            lines.append(line)
    return lines


from itertools import takewhile
def read_takewhile():
    with open("file.txt") as f:
        lines = deque(takewhile(lambda l: "error" not in l, f), maxlen=8)
    return lines
  • Related