【问题标题】:Simple Way of NOT reading last N lines of a file in Python在 Python 中不读取文件最后 N 行的简单方法
【发布时间】:2014-11-02 05:34:45
【问题描述】:

我想逐行读取文件,最后 N 行除外。在 Python 中,我如何知道在哪里停止,而不到达文件末尾并回溯/丢弃最后 N 行?要求#lines = X,并且循环(X-N)是解决这个问题的好方法吗?

最简单/最 Pythonic 的方法是什么?

【问题讨论】:

  • 一般来说,如果行可以是可变长度的,那么 没有办法,无论是 Python 还是其他方式,都无法知道文件的一部分中有多少行'不读。
  • 您可以使用readlines 读取文件,然后应用len 来获取文件中的总行数,现在您可以这样做了
  • @Hackaholic 您刚刚阅读了这些行...而不是 len 您可以将其切片 [:-N] ...这是“丢弃最后 N 行”...
  • 是的,切片会更好
  • 在某种程度上它可能看起来/我可能在问一个愚蠢的问题。毕竟,一行是'\n',而Python如何知道剩下多少,而不实际读取磁盘上的文件......所以大部分问题是关于如何优雅地做到这一点。跨度>

标签: python file-io


【解决方案1】:

三种不同的解决方案:

1) 又快又脏,见约翰的回答:

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 + n_skip 行存在时才能确定可以处理行i。这意味着您必须首先将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 行,但这仍然比在第一个解决方案中读取内存中的所有行要好。

这 3 种方法中哪一种最好是代码复杂性、内存和速度之间的权衡,这取决于您的具体应用程序。

【讨论】:

    【解决方案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,)
    

    【讨论】:

      【解决方案3】:

      要阅读直到最后 X 行的所有行,您需要知道最后 X 行的开始位置。您将在某处需要此信息。 有几种方法可以获取此信息。

      1. 当您写入文件时,保存最后 X 行的位置。到达该位置时停止阅读。
      2. 将行首的位置存储在某处,这样可以附加到文件中。
      3. 您知道线条的大小。
        1. 每一行可以有相同的大小,你可以根据文件大小来计算它
        2. 每行至少有一个字符,因此您无需阅读最后的 X 个字符。

      【讨论】:

        【解决方案4】:

        鉴于我们知道必须将文件读取到末尾以确定有多少行,这是我尝试以“最简单/最 Pythonic 的方式”读取最后 n 行:

        with open(foo, 'r') as f:
            lines = f.readlines()[:-n]
        

        【讨论】:

        • 当然,我不知道为什么我一开始没有这样写,我猜是累了:)
        • 在洛杉矶,嗯?在意大利,我们常常祝福“晚安,金梦!”
        • 这对于小文件来说是一个简单的解决方案,但是对于非常大的文件,您不想使用readlines() 读取内存中的所有行,您通常希望在读取它们时懒惰地处理它们.
        • @BasSwinckels 对于大文件来说确实如此,但这只是对最简单方法的尝试。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-15
        • 1970-01-01
        • 2010-09-07
        相关资源
        最近更新 更多