Как читать строки из файла на питоне, начиная с конца

Мне нужно знать, как читать строки из файла в python, чтобы я сначала прочитал последнюю строку и продолжаю так, пока курсор не достигнет начала файла. Есть идеи?

Ответ 1

Общий подход к этой проблеме, считывая текстовый файл в обратном порядке, по-разному, может быть решен, по крайней мере, тремя способами.

Общая проблема заключается в том, что, поскольку каждая строка может иметь разную длину, вы не можете заранее знать, где каждая строка начинается в файле, а также сколько из них есть. Это означает, что вам нужно применить некоторую логику к проблеме.

Общий подход # 1: чтение всего файла в память

При таком подходе вы просто читаете весь файл в памяти, в некоторой структуре данных, которая впоследствии позволяет обрабатывать список строк в обратном порядке. Это может быть стек, двусвязный список или даже массив.

Плюсы: Действительно легко реализовать (возможно, встроенный в Python для всех, кого я знаю)
Минусы: Использует много памяти, может потребоваться некоторое время для чтения больших файлов

Общий подход # 2: Прочитайте весь файл, сохраните позицию строк

При таком подходе вы также читаете весь файл один раз, но вместо хранения всего файла (всего текста) в памяти вы сохраняете только двоичные позиции внутри файла, где начиналась каждая строка. Вы можете сохранить эти позиции в аналогичной структуре данных, как та, которая хранит строки в первом подходе.

Вы хотите прочитать строку X, вам нужно перечитать строку из файла, начиная с позиции, которую вы сохранили для начала этой строки.

Плюсы: Почти так же легко реализовать как первый подход
Минусы: может потребоваться некоторое время для чтения больших файлов

Общий подход №3: Прочтите файл в обратном порядке и "выберите его"

При таком подходе вы будете читать фрагмент файла или аналогичный, с конца, и посмотреть, где находятся концы. У вас в основном есть буфер, скажем, 4096 байт, и обрабатывать последнюю строку этого буфера. Когда ваша обработка, которая должна перемещать одну строку за раз назад в этом буфере, подходит к началу буфера, вам нужно прочитать другую ценность данных в буфере, от области до первого прочитанного буфера и продолжить обработку.

Этот подход, как правило, более сложный, потому что вам нужно обрабатывать такие вещи, как линии, разбитые на два буфера, а длинные строки могут даже охватывать более двух буферов.

Это, однако, тот, который потребует наименьшего объема памяти и для действительно больших файлов, также может быть полезно сделать это, чтобы сначала не считывать гигабайты информации.

Плюсы: Использует небольшую память, не требует, чтобы вы сначала прочитали весь файл

Минусы: Многое трудно реализовать и получить право на все угловые случаи


В сети есть множество ссылок, в которых показано, как сделать третий подход:

Ответ 3

Вы также можете использовать модуль 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

    # do it again
    for l in frb:
         print l

Дополнительную документацию можно найти на http://file-read-backwards.readthedocs.io/en/latest/readme.html

Ответ 4

Прямым способом является создание временного файла с переворачиванием, а затем изменение каждой строки в этом файле.

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).

Ответ 5

Это решение проще, чем любые другие, которые я видел.

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)