如何从最后开始从python中读取文件中的行

时间:2010-08-25 17:55:14

标签: python file-io

我需要知道如何从python中的文件读取行,以便我首先读取最后一行并以这种方式继续,直到光标到达文件的开头。有任何想法吗?

5 个答案:

答案 0 :(得分:23)

解决这个问题的一般方法是,按行,反向读取文本文件,至少可以通过三种方法解决。

一般问题是,由于每条线的长度可能不同,因此您无法事先知道每行在文件中的起始位置,也不知道其中有多少行。这意味着您需要对问题应用一些逻辑。

一般方法#1:将整个文件读入内存

使用这种方法,您只需将整个文件读入内存,在某些数据结构中随后允许您反向处理行列表。堆栈,双向链表,甚至数组都可以做到这一点。

优点:非常容易实现(可能是我所知道的所有内置于Python中)
缺点:使用大量内存,可能需要一段时间才能读取大文件

一般方法#2:读取整个文件,存储行的位置

使用这种方法,您还可以读取整个文件一次,但不是将整个文件(所有文本)存储在内存中,而是仅将二进制位置存储在每行开始的文件中。您可以将这些位置存储在与第一种方法中存储行的数据结构类似的数据结构中。

当您想要读取X行时,您必须从文件中重新读取该行,从您为该行开头存储的位置开始。

优点:与第一种方法一样易于实施 缺点:可能需要一段时间才能阅读大文件

一般方法#3:反向读取文件,并“弄明白”

使用这种方法,您将从最后逐块或类似地读取文件,并查看结尾的位置。你基本上有一个缓冲区,比如4096字节,并处理该缓冲区的最后一行。当您的处理(必须在该缓冲区中一次向后移动一行)进入缓冲区的开始时,您需要从您读取的第一个缓冲区之前的区域读取另一个缓冲区的数据,然后继续处理。

这种方法通常比较复杂,因为你需要处理诸如两个缓冲区中断的行,而长行甚至可以覆盖两个以上的缓冲区。

然而,它是需要最少内存的那个,对于非常大的文件,也可能值得这样做以避免首先读取千兆字节的信息。

优点:使用少量内存,不需要您先读取整个文件 缺点:很难实现并适合所有角落案件


网上有很多链接,展示了如何进行第三种方法:

答案 1 :(得分:4)

您也可以使用python模块file_read_backwards。它将以内存有效的方式读取。它适用于Python 2.7和3。

它支持" utf-8"," latin-1"和" ascii"编码。它适用于" \ r"," \ n"和" \ r \ n"作为新线。

安装后,通过pip install file_read_backwards(v1.2.1),您可以通过以下方式向后(按行)读取整个文件:

#!/usr/bin/env python2.7

from file_read_backwards import FileReadBackwards

with FileReadBackwards("/path/to/file", encoding="utf-8") as frb:
    for l in frb:
         print l

可在http://file-read-backwards.readthedocs.io/en/latest/readme.html

找到更多文档

答案 2 :(得分:3)

答案 3 :(得分:1)

一种简单的方法是首先创建一个临时反转文件,然后反转该文件中的每一行。

import os, tempfile

def reverse_file(in_filename, fout, blocksize=1024):
    filesize = os.path.getsize(in_filename)
    fin = open(in_filename, 'rb')
    for i in range(filesize // blocksize, -1, -1):
        fin.seek(i * blocksize)
        data = fin.read(blocksize)
        fout.write(data[::-1])

def enumerate_reverse_lines(in_filename, blocksize=1024):
    fout = tempfile.TemporaryFile()
    reverse_file(in_filename, fout, blocksize=blocksize)
    fout.seek(0)
    for line in fout:
        yield line[::-1]

上面的代码将在开头而不是结尾处生成带换行符的行,并且不会尝试处理DOS / Windows样式的换行符(\ r \ n)。

答案 4 :(得分:1)

这个解决方案比我见过的任何其他解决方案都简单。

def xreadlines_reverse(f, blksz=524288):
    "Act as a generator to return the lines in file f in reverse order."
    buf = ""
    f.seek(0, 2)
    pos = f.tell()
    lastn = 0
    if pos == 0:
        pos = -1
    while pos != -1:
        nlpos = buf.rfind("\n", 0, -1)
        if nlpos != -1:
            line = buf[nlpos + 1:]
            if line[-1] != "\n":
                line += "\n"
            buf = buf[:nlpos + 1]
            yield line
        elif pos == 0:
            pos = -1
            yield buf
        else:
            n = min(blksz, pos)
            f.seek(-(n + lastn), 1)
            rdbuf = f.read(n)
            lastn = len(rdbuf)
            buf = rdbuf + buf
            pos -= n

使用示例:

for line in xreadlines_reverse(open("whatever.txt")):
    do_stuff(line)