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