我希望逐行读取文件,除了最后N行。我怎么知道在哪里停止,没有到达文件的末尾并回溯跟踪/丢弃最后的N行,在Python中?要求#lines = X,并且循环(X-N)是一个很好的方法来解决这个问题吗?
最简单/最恐怖的方式是什么?
答案 0 :(得分:2)
除非您事先知道实际行数,否则您必须阅读整个文件。
但是我假设您希望处理除了N最后一行之外的行文件,您可以在不加载内存中的所有文件的情况下执行此操作,并且只保留N行的列表:
with open(file) as fd:
lines = []
try:
for i in range(N):
lines.append(next(fd))
i = 0
for line in fd:
# process lines[i]
print (lines[i].rstrip())
lines[i] = line
i = (i + 1) % N
except StopIteration:
print "less than %d lines" % (N,)
答案 1 :(得分:2)
三种不同的解决方案:
1)快速而肮脏,请参阅John的回答:
with open(file_name) as fid:
lines = fid.readlines()
for line in lines[:-n_skip]:
do_something_with(line)
这种方法的缺点是你必须首先读取内存中的所有行,这可能是大文件的问题。
2)两次通过
处理文件两次,一次计算行数n_lines
,在第二次处理过程中只计算第一行n_lines - n_skip
行:
# first pass to count
with open(file_name) as fid:
n_lines = sum(1 for line in fid)
# second pass to actually do something
with open(file_name) as fid:
for i_line in xrange(n_lines - n_skip): # does nothing if n_lines <= n_skip
line = fid.readline()
do_something_with(line)
此方法的缺点是您必须迭代文件两次,在某些情况下可能会更慢。然而,好处是你的内存中永远不会有多行。
3)使用缓冲区,类似于Serge的解决方案
如果您只想对文件进行一次迭代,只有在知道行i
存在时才能确定可以处理行i + n_skip
。这意味着您必须先将n_skip
行保留在临时缓冲区中。一种方法是实现某种FIFO缓冲区(例如,使用实现循环缓冲区的生成器函数):
def fifo(it, n):
buffer = [None] * n # preallocate buffer
i = 0
full = False
for item in it: # leaves last n items in buffer when iterator is exhausted
if full:
yield buffer[i] # yield old item before storing new item
buffer[i] = item
i = (i + 1) % n
if i == 0: # wrapped around at least once
full = True
使用一系列数字进行快速测试:
In [12]: for i in fifo(range(20), 5):
...: print i,
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
您在文件中使用它的方式:
with open(file_name) as fid:
for line in fifo(fid, n_skip):
do_something_with(line)
请注意,这需要足够的内存来临时存储n_skip
行,但这仍然比在第一个解决方案中读取内存中的所有行更好。
这三种方法中哪一种最好是在代码复杂性,内存和速度之间进行权衡,这取决于您的确切应用。
答案 2 :(得分:1)
要读取最后X行的所有行,您需要知道最后X行的开始位置。您将在某处获得此信息。 有几种方法可以获取此信息。
答案 3 :(得分:1)
鉴于我们知道文件必须被读到最后才能确定有多少行,这是我尝试阅读最后n
行的“最简单/最恐怖的方式”:
with open(foo, 'r') as f:
lines = f.readlines()[:-n]