Como ler um arquivo na ordem inversa?


128

Como ler um arquivo na ordem inversa usando python? Quero ler um arquivo da última linha para a primeira linha.


7
Você quer dizer "leia na ordem inversa" ou "processe as linhas na ordem inversa"? Há uma diferença. Com o primeiro, potencialmente, o arquivo não caberia na memória ao mesmo tempo, portanto, você deseja processar as linhas na ordem inversa, mas não pode ler e inverter o arquivo inteiro. Com o segundo, você pode apenas ler o arquivo inteiro e reverter a lista de linhas antes de processá-los. Então qual é?
Lasse V. Karlsen


1
Eu recomendo isso - sem problemas de memória e rápido: stackoverflow.com/a/260433/1212562
Brian B

Respostas:


73
for line in reversed(open("filename").readlines()):
    print line.rstrip()

E no Python 3:

for line in reversed(list(open("filename"))):
    print(line.rstrip())

192
Infelizmente, isso não funciona se você não pode ajustar o arquivo inteiro na memória.
vy32

3
Além disso, embora o código publicado responda à pergunta, devemos ter o cuidado de fechar os arquivos que abrimos. A withafirmação é geralmente bastante indolor.
William William

1
@ MichaelDavidWatson: Não sem antes ler o iterador original na memória e depois apresentar um novo iterador sobre o primeiro em sentido inverso.
precisa

3
@ MichaelDavidWatson: Você pode ler um arquivo ao contrário, sem lê-lo na memória, mas não é trivial e requer muitas travessuras de buffer para evitar um desperdício considerável de chamadas do sistema. Ele também terá um desempenho muito ruim (embora melhor do que ler toda a memória na memória se o arquivo exceder a memória disponível).
precisa

1
@ William Desculpe, como eu uso a solução acima usando "with open" enquanto itera sobre o arquivo e depois limpa-o?
BringBackCommodore64

146

Uma resposta correta e eficiente escrita como um gerador.

import os

def reverse_readline(filename, buf_size=8192):
    """A generator that returns the lines of a file in reverse order"""
    with open(filename) as fh:
        segment = None
        offset = 0
        fh.seek(0, os.SEEK_END)
        file_size = remaining_size = fh.tell()
        while remaining_size > 0:
            offset = min(file_size, offset + buf_size)
            fh.seek(file_size - offset)
            buffer = fh.read(min(remaining_size, buf_size))
            remaining_size -= buf_size
            lines = buffer.split('\n')
            # The first line of the buffer is probably not a complete line so
            # we'll save it and append it to the last line of the next buffer
            # we read
            if segment is not None:
                # If the previous chunk starts right from the beginning of line
                # do not concat the segment to the last line of new chunk.
                # Instead, yield the segment first 
                if buffer[-1] != '\n':
                    lines[-1] += segment
                else:
                    yield segment
            segment = lines[0]
            for index in range(len(lines) - 1, 0, -1):
                if lines[index]:
                    yield lines[index]
        # Don't yield None if the file was empty
        if segment is not None:
            yield segment

4
Isso não funcionará para arquivos de texto em python> = 3.2, pois, por algum motivo, as buscas relacionadas ao final do arquivo não são mais suportadas. Pode ser corrigido salvando o tamanho do arquivo retornado fh.seek(0, os.SEEK_END)e alterando fh.seek(-offset, os.SEEK_END)também fh.seek(file_size - offset).
levesque

9
Após as edições feitas, isso funciona perfeitamente no python 3.5. Melhor resposta para a pergunta.
precisa saber é o seguinte

3
reverter essa mudança para Python 2, onde fh.seek()retornosNone
marengaz

1
Observe que isso pode não funcionar como esperado para arquivos de texto. Obter blocos corretamente na ordem inversa funciona apenas para arquivos binários. O problema é que, para arquivos de texto com codificação multi-byte (como utf8), seek()e read()referem-se a diferentes tamanhos. Essa é provavelmente também a razão pela qual o primeiro argumento diferente de zero de seek()relativo a os.SEEK_ENDnão é suportado.
Norok2 23/07/1918

3
simples: 'aöaö'.encode()é b'a\xc3\xb6a\xc3\xb6'. Se você salvar isso em disco e depois ler no modo de texto, ao fazer seek(2)isso, ele será movido por dois bytes, o que seek(2); read(1)resultará em um erro UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 0: invalid start byte, mas se você fizer isso seek(0); read(2); read(1), obterá o 'a'que esperava, ou seja: seek()nunca é codificado , read()se você abrir o arquivo no modo de texto. Agora, se houver 'aöaö' * 1000000, seus blocos não serão alinhados corretamente.
Norok2

23

Que tal algo como isso:

import os


def readlines_reverse(filename):
    with open(filename) as qfile:
        qfile.seek(0, os.SEEK_END)
        position = qfile.tell()
        line = ''
        while position >= 0:
            qfile.seek(position)
            next_char = qfile.read(1)
            if next_char == "\n":
                yield line[::-1]
                line = ''
            else:
                line += next_char
            position -= 1
        yield line[::-1]


if __name__ == '__main__':
    for qline in readlines_reverse(raw_input()):
        print qline

Como o arquivo é lido caractere por caractere na ordem inversa, ele funcionará mesmo em arquivos muito grandes, desde que linhas individuais se ajustem à memória.


20

Você também pode usar o módulo python file_read_backwards.

Após a instalação, via pip install file_read_backwards(v1.2.1), você pode ler o arquivo inteiro de trás para a frente (em linhas) de uma maneira eficiente em termos de memória, através de:

#!/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

Ele suporta as codificações "utf-8", "latin-1" e "ascii".

O suporte também está disponível para python3. Mais documentação pode ser encontrada em http://file-read-backwards.readthedocs.io/en/latest/readme.html


Obrigado por esta solução. Eu gosto (e também votei) da solução acima por @srohde, pois me ajudou a entender como é feito, mas como desenvolvedor, prefiro usar um módulo existente quando posso, por isso estou feliz em saber sobre esse.
Joanis

1
Isso funciona com codificação multibyte como UTF-8. A solução de busca / leitura não: seek () conta em bytes, read () em caracteres.
Jeremitu 27/07/19

9
for line in reversed(open("file").readlines()):
    print line.rstrip()

Se você estiver no linux, poderá usar o taccomando

$ tac file

2 receitas que você pode encontrar no ActiveState aqui e aqui


1
Gostaria de saber se reversed () consome toda a sequência antes da iteração. Os documentos dizem que um __reversed__()método é necessário, mas o python2.5 não se queixa de uma classe personalizada sem ele.
muhuk

@muhuk, que provavelmente tem para armazenar em cache-lo de alguma forma, eu suspeito que gera uma nova lista em ordem inversa, em seguida, retorna um iterador para que
Matt Joiner

1
@ Matt: isso seria ridículo. Simplesmente vai de trás para frente - len (L) -1 é a parte de trás, 0 é a frente. Você pode imaginar o resto.
Devin Jeanpierre

@muhuk: As sequências não são consumidas de maneira significativa (você pode repetir a sequência inteira, mas isso não importa muito). Um __reversed__método também não é necessário e não costumava ser assim. Se um objeto fornece __len__e __getitem__funcionará bem (menos alguns casos excepcionais, como dict).
Devin Jeanpierre

@ Kevin Jeanpierre: Somente se readlines () retornar um objeto que fornece __reversed__?
Matt Joiner

8
import re

def filerev(somefile, buffer=0x20000):
  somefile.seek(0, os.SEEK_END)
  size = somefile.tell()
  lines = ['']
  rem = size % buffer
  pos = max(0, (size // buffer - 1) * buffer)
  while pos >= 0:
    somefile.seek(pos, os.SEEK_SET)
    data = somefile.read(rem + buffer) + lines[0]
    rem = 0
    lines = re.findall('[^\n]*\n?', data)
    ix = len(lines) - 2
    while ix > 0:
      yield lines[ix]
      ix -= 1
    pos -= buffer
  else:
    yield lines[0]

with open(sys.argv[1], 'r') as f:
  for line in filerev(f):
    sys.stdout.write(line)

Isso parece produzir a saída incorreta para arquivos maiores que o buffer. Ele não manipula corretamente as linhas que abrangem os pedaços do tamanho do buffer em que você lê, como eu o entendo. Postei outra resposta semelhante (para outra pergunta semelhante).
Darius Bacon

@ Darius: Ah sim, parece que perdi um pouco. Deve ser corrigido agora.
Ignacio Vazquez-Abrams

Parece certo. Eu ainda preferiria meu próprio código, porque isso O (N ^ 2) funciona em um arquivo grande que é uma linha longa. (Nas respostas semelhantes para a outra pergunta que eu testei isso causou uma séria desaceleração genuíno sobre esses arquivos.)
Darius Bacon

3
Bem, a questão não mencionou performance, então eu não posso procurar defeitos do desastre desempenho que é expressões regulares: P
Matt Joiner

Alguma explicação a mais seria útil como desempenho e se isso realmente puder procurar dizer a última linha e ler apenas essa peça.
User1767754

7

A resposta aceita não funcionará para casos com arquivos grandes que não cabem na memória (o que não é um caso raro).

Como foi observado por outros, a resposta @srohde parece boa, mas tem os próximos problemas:

  • o arquivo de abertura parece redundante, quando podemos passar o objeto de arquivo e deixar para o usuário decidir em qual codificação deve ser lida,
  • mesmo se refatorarmos para aceitar o objeto de arquivo, ele não funcionará para todas as codificações: podemos escolher um arquivo com utf-8conteúdo de codificação e não-ascii, como

    й

    passar buf_sizeigual 1e terá

    UnicodeDecodeError: 'utf8' codec can't decode byte 0xb9 in position 0: invalid start byte

    é claro que o texto pode ser maior, mas buf_sizepode ser coletado, resultando em erro ofuscado, como acima,

  • não podemos especificar separador de linha personalizado,
  • não podemos optar por manter o separador de linhas.

Portanto, considerando todas essas preocupações, escrevi funções separadas:

  • um que trabalha com fluxos de bytes,
  • o segundo que trabalha com fluxos de texto e delega seu fluxo de bytes subjacente ao primeiro e decodifica as linhas resultantes.

Antes de tudo, vamos definir as próximas funções utilitárias:

ceil_divisionpara fazer divisão com teto (em contraste com a //divisão padrão com piso, mais informações podem ser encontradas neste tópico )

def ceil_division(left_number, right_number):
    """
    Divides given numbers with ceiling.
    """
    return -(-left_number // right_number)

split para dividir a string pelo separador fornecido da extremidade direita, com capacidade de mantê-la:

def split(string, separator, keep_separator):
    """
    Splits given string by given separator.
    """
    parts = string.split(separator)
    if keep_separator:
        *parts, last_part = parts
        parts = [part + separator for part in parts]
        if last_part:
            return parts + [last_part]
    return parts

read_batch_from_end ler lote da extremidade direita do fluxo binário

def read_batch_from_end(byte_stream, size, end_position):
    """
    Reads batch from the end of given byte stream.
    """
    if end_position > size:
        offset = end_position - size
    else:
        offset = 0
        size = end_position
    byte_stream.seek(offset)
    return byte_stream.read(size)

Depois disso, podemos definir a função para ler o fluxo de bytes em ordem inversa, como

import functools
import itertools
import os
from operator import methodcaller, sub


def reverse_binary_stream(byte_stream, batch_size=None,
                          lines_separator=None,
                          keep_lines_separator=True):
    if lines_separator is None:
        lines_separator = (b'\r', b'\n', b'\r\n')
        lines_splitter = methodcaller(str.splitlines.__name__,
                                      keep_lines_separator)
    else:
        lines_splitter = functools.partial(split,
                                           separator=lines_separator,
                                           keep_separator=keep_lines_separator)
    stream_size = byte_stream.seek(0, os.SEEK_END)
    if batch_size is None:
        batch_size = stream_size or 1
    batches_count = ceil_division(stream_size, batch_size)
    remaining_bytes_indicator = itertools.islice(
            itertools.accumulate(itertools.chain([stream_size],
                                                 itertools.repeat(batch_size)),
                                 sub),
            batches_count)
    try:
        remaining_bytes_count = next(remaining_bytes_indicator)
    except StopIteration:
        return

    def read_batch(position):
        result = read_batch_from_end(byte_stream,
                                     size=batch_size,
                                     end_position=position)
        while result.startswith(lines_separator):
            try:
                position = next(remaining_bytes_indicator)
            except StopIteration:
                break
            result = (read_batch_from_end(byte_stream,
                                          size=batch_size,
                                          end_position=position)
                      + result)
        return result

    batch = read_batch(remaining_bytes_count)
    segment, *lines = lines_splitter(batch)
    yield from reverse(lines)
    for remaining_bytes_count in remaining_bytes_indicator:
        batch = read_batch(remaining_bytes_count)
        lines = lines_splitter(batch)
        if batch.endswith(lines_separator):
            yield segment
        else:
            lines[-1] += segment
        segment, *lines = lines
        yield from reverse(lines)
    yield segment

e, finalmente, uma função para reverter o arquivo de texto pode ser definida como:

import codecs


def reverse_file(file, batch_size=None, 
                 lines_separator=None,
                 keep_lines_separator=True):
    encoding = file.encoding
    if lines_separator is not None:
        lines_separator = lines_separator.encode(encoding)
    yield from map(functools.partial(codecs.decode,
                                     encoding=encoding),
                   reverse_binary_stream(
                           file.buffer,
                           batch_size=batch_size,
                           lines_separator=lines_separator,
                           keep_lines_separator=keep_lines_separator))

Testes

Preparações

Eu criei 4 arquivos usando o fsutilcomando :

  1. empty.txt sem conteúdo, tamanho 0MB
  2. tiny.txt com tamanho de 1MB
  3. small.txt com tamanho de 10 MB
  4. large.txt com tamanho de 50MB

Também refatorei a solução @srohde para trabalhar com o objeto de arquivo em vez do caminho do arquivo.

Script de teste

from timeit import Timer

repeats_count = 7
number = 1
create_setup = ('from collections import deque\n'
                'from __main__ import reverse_file, reverse_readline\n'
                'file = open("{}")').format
srohde_solution = ('with file:\n'
                   '    deque(reverse_readline(file,\n'
                   '                           buf_size=8192),'
                   '          maxlen=0)')
azat_ibrakov_solution = ('with file:\n'
                         '    deque(reverse_file(file,\n'
                         '                       lines_separator="\\n",\n'
                         '                       keep_lines_separator=False,\n'
                         '                       batch_size=8192), maxlen=0)')
print('reversing empty file by "srohde"',
      min(Timer(srohde_solution,
                create_setup('empty.txt')).repeat(repeats_count, number)))
print('reversing empty file by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('empty.txt')).repeat(repeats_count, number)))
print('reversing tiny file (1MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('tiny.txt')).repeat(repeats_count, number)))
print('reversing tiny file (1MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('tiny.txt')).repeat(repeats_count, number)))
print('reversing small file (10MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('small.txt')).repeat(repeats_count, number)))
print('reversing small file (10MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('small.txt')).repeat(repeats_count, number)))
print('reversing large file (50MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('large.txt')).repeat(repeats_count, number)))
print('reversing large file (50MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('large.txt')).repeat(repeats_count, number)))

Nota : Eu usei collections.dequeclasse para esgotar o gerador.

Saídas

Para o PyPy 3.5 no Windows 10:

reversing empty file by "srohde" 8.31e-05
reversing empty file by "Azat Ibrakov" 0.00016090000000000028
reversing tiny file (1MB) by "srohde" 0.160081
reversing tiny file (1MB) by "Azat Ibrakov" 0.09594989999999998
reversing small file (10MB) by "srohde" 8.8891863
reversing small file (10MB) by "Azat Ibrakov" 5.323388100000001
reversing large file (50MB) by "srohde" 186.5338368
reversing large file (50MB) by "Azat Ibrakov" 99.07450229999998

Para o CPython 3.5 no Windows 10:

reversing empty file by "srohde" 3.600000000000001e-05
reversing empty file by "Azat Ibrakov" 4.519999999999958e-05
reversing tiny file (1MB) by "srohde" 0.01965560000000001
reversing tiny file (1MB) by "Azat Ibrakov" 0.019207699999999994
reversing small file (10MB) by "srohde" 3.1341862999999996
reversing small file (10MB) by "Azat Ibrakov" 3.0872588000000007
reversing large file (50MB) by "srohde" 82.01206720000002
reversing large file (50MB) by "Azat Ibrakov" 82.16775059999998

Então, como podemos ver, ele funciona como solução original, mas é mais geral e livre de suas desvantagens listadas acima.


Propaganda

Adicionei isso à 0.3.0versão do lzpacote (requer Python 3.5 +) que possui muitos utilitários funcionais / de iteração bem testados.

Pode ser usado como

 import io
 from lz.iterating import reverse
 ...
 with open('path/to/file') as file:
     for line in reverse(file, batch_size=io.DEFAULT_BUFFER_SIZE):
         print(line)

Ele suporta todas as codificações padrão (talvez, exceto utf-7porque é difícil para mim definir uma estratégia para gerar cadeias codificáveis ​​com ela).


2

Aqui você pode encontrar minha implementação, você pode limitar o uso de memória RAM alterando a variável "buffer"; existe um erro que o programa imprime uma linha vazia no início.

E também o uso de memória RAM pode aumentar se não houver novas linhas com mais de bytes de buffer, a variável "leak" aumentará até ver uma nova linha ("\ n").

Isso também funciona para arquivos de 16 GB, que são maiores que minha memória total.

import os,sys
buffer = 1024*1024 # 1MB
f = open(sys.argv[1])
f.seek(0, os.SEEK_END)
filesize = f.tell()

division, remainder = divmod(filesize, buffer)
line_leak=''

for chunk_counter in range(1,division + 2):
    if division - chunk_counter < 0:
        f.seek(0, os.SEEK_SET)
        chunk = f.read(remainder)
    elif division - chunk_counter >= 0:
        f.seek(-(buffer*chunk_counter), os.SEEK_END)
        chunk = f.read(buffer)

    chunk_lines_reversed = list(reversed(chunk.split('\n')))
    if line_leak: # add line_leak from previous chunk to beginning
        chunk_lines_reversed[0] += line_leak

    # after reversed, save the leakedline for next chunk iteration
    line_leak = chunk_lines_reversed.pop()

    if chunk_lines_reversed:
        print "\n".join(chunk_lines_reversed)
    # print the last leaked line
    if division - chunk_counter < 0:
        print line_leak

2

Obrigado pela resposta @srohde. Ele tem um pequeno erro ao verificar o caractere de nova linha com o operador 'is' e não pude comentar a resposta com 1 reputação. Também gostaria de gerenciar a abertura de arquivos do lado de fora, porque isso me permite incorporar minhas divagações para tarefas luigi.

O que eu precisava mudar tem a forma:

with open(filename) as fp:
    for line in fp:
        #print line,  # contains new line
        print '>{}<'.format(line)

Gostaria de mudar para:

with open(filename) as fp:
    for line in reversed_fp_iter(fp, 4):
        #print line,  # contains new line
        print '>{}<'.format(line)

Aqui está uma resposta modificada que deseja manipular um arquivo e mantém novas linhas:

def reversed_fp_iter(fp, buf_size=8192):
    """a generator that returns the lines of a file in reverse order
    ref: https://stackoverflow.com/a/23646049/8776239
    """
    segment = None  # holds possible incomplete segment at the beginning of the buffer
    offset = 0
    fp.seek(0, os.SEEK_END)
    file_size = remaining_size = fp.tell()
    while remaining_size > 0:
        offset = min(file_size, offset + buf_size)
        fp.seek(file_size - offset)
        buffer = fp.read(min(remaining_size, buf_size))
        remaining_size -= buf_size
        lines = buffer.splitlines(True)
        # the first line of the buffer is probably not a complete line so
        # we'll save it and append it to the last line of the next buffer
        # we read
        if segment is not None:
            # if the previous chunk starts right from the beginning of line
            # do not concat the segment to the last line of new chunk
            # instead, yield the segment first
            if buffer[-1] == '\n':
                #print 'buffer ends with newline'
                yield segment
            else:
                lines[-1] += segment
                #print 'enlarged last line to >{}<, len {}'.format(lines[-1], len(lines))
        segment = lines[0]
        for index in range(len(lines) - 1, 0, -1):
            if len(lines[index]):
                yield lines[index]
    # Don't yield None if the file was empty
    if segment is not None:
        yield segment

1

uma função simples para criar um segundo arquivo invertido (somente linux):

import os
def tac(file1, file2):
     print(os.system('tac %s > %s' % (file1,file2)))

Como usar

tac('ordered.csv', 'reversed.csv')
f = open('reversed.csv')

Eu acho que o objetivo era como fazê-lo em Python. Além disso, isso funciona apenas em sistemas * Nix, embora seja uma excelente solução para isso. É basicamente apenas usando Python como um prompt para executar utilitários de shell.
Alexander Huszagh 24/02

1
Este código possui grandes erros de segurança, como está escrito atualmente. E se você estiver tentando reverter um arquivo criado mv mycontent.txt $'hello $(rm -rf $HOME) world.txt'ou usando um nome de arquivo de saída fornecido por um usuário não confiável? Se você deseja manipular nomes de arquivos arbitrários com segurança, é preciso mais cuidado. subprocess.Popen(['tac', file1], stdout=open(file2, 'w'))estaria seguro, por exemplo.
Charles Duffy

O código existente também não manipula corretamente arquivos com espaços, curingas, etc.
Charles Duffy


1

com aberto ("nome do arquivo") como f:

    print(f.read()[::-1])

Isso lê o arquivo inteiro? Isso é seguro em arquivos grandes? Esta parece ser uma maneira muito fácil e realista de fazer isso, mas não tenho certeza sobre as perguntas acima. Gostaria de pesquisar o arquivo dessa maneira (usando re) ..
ikwyl6 31/01

@ ikwyl6 Isso deve ser equivalente a list(reversed(f.read())).
AMC

0
def reverse_lines(filename):
    y=open(filename).readlines()
    return y[::-1]

Esta não é essencialmente a mesma solução que a resposta aceita ?
AMC

0

Sempre use withao trabalhar com arquivos, pois lida com tudo para você:

with open('filename', 'r') as f:
    for line in reversed(f.readlines()):
        print line

Ou no Python 3:

with open('filename', 'r') as f:
    for line in reversed(list(f.readlines())):
        print(line)

0

você precisaria primeiro abrir seu arquivo no formato de leitura, salvá-lo em uma variável e, em seguida, abrir o segundo arquivo no formato de gravação em que gravaria ou acrescentaria a variável usando uma fatia [:: - 1], revertendo completamente o arquivo. Você também pode usar readlines () para torná-lo uma lista de linhas, que você pode manipular

def copy_and_reverse(filename, newfile):
    with open(filename) as file:
        text = file.read()
    with open(newfile, "w") as file2:
        file2.write(text[::-1])

0

A maioria das respostas precisa ler o arquivo inteiro antes de fazer qualquer coisa. Esta amostra lê amostras cada vez maiores do final .

Eu só vi a resposta de Murat Yükselen enquanto escrevia essa resposta. É quase o mesmo, o que eu acho que é uma coisa boa. A amostra abaixo também lida com \ re aumenta seu tamanho do buffer em cada etapa. Eu também tenho alguns testes de unidade para fazer backup desse código.

def readlines_reversed(f):
    """ Iterate over the lines in a file in reverse. The file must be
    open in 'rb' mode. Yields the lines unencoded (as bytes), including the
    newline character. Produces the same result as readlines, but reversed.
    If this is used to reverse the line in a file twice, the result is
    exactly the same.
    """
    head = b""
    f.seek(0, 2)
    t = f.tell()
    buffersize, maxbuffersize = 64, 4096
    while True:
        if t <= 0:
            break
        # Read next block
        buffersize = min(buffersize * 2, maxbuffersize)
        tprev = t
        t = max(0, t - buffersize)
        f.seek(t)
        lines = f.read(tprev - t).splitlines(True)
        # Align to line breaks
        if not lines[-1].endswith((b"\n", b"\r")):
            lines[-1] += head  # current tail is previous head
        elif head == b"\n" and lines[-1].endswith(b"\r"):
            lines[-1] += head  # Keep \r\n together
        elif head:
            lines.append(head)
        head = lines.pop(0)  # can be '\n' (ok)
        # Iterate over current block in reverse
        for line in reversed(lines):
            yield line
    if head:
        yield head

0

Leia o arquivo linha por linha e adicione-o em uma lista na ordem inversa.

Aqui está um exemplo de código:

reverse = []
with open("file.txt", "r") as file:
    for line in file:
        line = line.strip()
         reverse[0:0] = line

Isso apenas parece uma versão inferior da solução na resposta aceita .
AMC


0
def previous_line(self, opened_file):
        opened_file.seek(0, os.SEEK_END)
        position = opened_file.tell()
        buffer = bytearray()
        while position >= 0:
            opened_file.seek(position)
            position -= 1
            new_byte = opened_file.read(1)
            if new_byte == self.NEW_LINE:
                parsed_string = buffer.decode()
                yield parsed_string
                buffer = bytearray()
            elif new_byte == self.EMPTY_BYTE:
                continue
            else:
                new_byte_array = bytearray(new_byte)
                new_byte_array.extend(buffer)
                buffer = new_byte_array
        yield None

usar:

opened_file = open(filepath, "rb")
iterator = self.previous_line(opened_file)
line = next(iterator) #one step
close(opened_file)

-3

Eu tive que fazer isso há algum tempo e usei o código abaixo. Tubos para a concha. Receio não ter mais o script completo. Se você estiver em um sistema operacional unixish, poderá usar "tac"; no entanto, por exemplo, no comando Mac OSX tac não funciona, use tail -r. O trecho de código abaixo testa qual plataforma você está e ajusta o comando de acordo

# We need a command to reverse the line order of the file. On Linux this
# is 'tac', on OSX it is 'tail -r'
# 'tac' is not supported on osx, 'tail -r' is not supported on linux.

if sys.platform == "darwin":
    command += "|tail -r"
elif sys.platform == "linux2":
    command += "|tac"
else:
    raise EnvironmentError('Platform %s not supported' % sys.platform)

O pôster está procurando uma resposta em python.
Mikemaccana

Bem, é uma resposta em Python, embora pareça incompleta.
DrDee 26/03

2
não é, não CrossPlatform, usando os comandos do sistema = não pythônico
Phyo Arkar Lwin

O pôster está procurando uma resposta "usando python", na qual o trecho de código está realmente escrito. Mas eu concordo que essa não é uma solução muito boa em comparação com muitas das outras postadas.
jeorgen

1
O snippet não é completo o suficiente para avaliar a correção (outras partes da invocação não são mostradas), mas o armazenamento de comandos shell em strings é altamente suspeito - é fácil ter bugs de injeção de shell, a menos que se trate muito de cuidados.
Charles Duffy
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.