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 é?
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 readif segment isnotNone:# 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 emptyif segment isnotNone:yield segment
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 -=1yield 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.
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.7from file_read_backwards importFileReadBackwardswithFileReadBackwards("/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".
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
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)-2while 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
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 importTimer
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)
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 lineif division - chunk_counter <0:print line_leak
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 lineprint'>{}<'.format(line)
Gostaria de mudar para:
with open(filename)as fp:for line in reversed_fp_iter(fp,4):#print line, # contains new lineprint'>{}<'.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 readif segment isnotNone:# 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 firstif 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 emptyif segment isnotNone:yield segment
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
Se você estiver preocupado com o tamanho do arquivo / uso da memória, mapear a memória do arquivo e digitalizar para trás em busca de novas linhas é uma solução:
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())).
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
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,4096whileTrue: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 breaksifnot lines[-1].endswith((b"\n", b"\r")):
lines[-1]+= head # current tail is previous headelif head == b"\n"and lines[-1].endswith(b"\r"):
lines[-1]+= head # Keep \r\n togetherelif head:
lines.append(head)
head = lines.pop(0)# can be '\n' (ok)# Iterate over current block in reversefor line in reversed(lines):yield line
if head:yield head
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:raiseEnvironmentError('Platform %s not supported'% sys.platform)
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.
Respostas:
E no Python 3:
fonte
with
afirmação é geralmente bastante indolor.Uma resposta correta e eficiente escrita como um gerador.
fonte
fh.seek(0, os.SEEK_END)
e alterandofh.seek(-offset, os.SEEK_END)
tambémfh.seek(file_size - offset)
.fh.seek()
retornosNone
utf8
),seek()
eread()
referem-se a diferentes tamanhos. Essa é provavelmente também a razão pela qual o primeiro argumento diferente de zero deseek()
relativo aos.SEEK_END
não é suportado.'aöaö'.encode()
éb'a\xc3\xb6a\xc3\xb6'
. Se você salvar isso em disco e depois ler no modo de texto, ao fazerseek(2)
isso, ele será movido por dois bytes, o queseek(2); read(1)
resultará em um erroUnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 0: invalid start byte
, mas se você fizer issoseek(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.Que tal algo como isso:
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.
fonte
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: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
fonte
Se você estiver no linux, poderá usar o
tac
comando2 receitas que você pode encontrar no ActiveState aqui e aqui
fonte
__reversed__()
método é necessário, mas o python2.5 não se queixa de uma classe personalizada sem ele.__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).__reversed__
?fonte
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:
mesmo se refatorarmos para aceitar o objeto de arquivo, ele não funcionará para todas as codificações: podemos escolher um arquivo com
utf-8
conteúdo de codificação e não-ascii, comopassar
buf_size
igual1
e teráé claro que o texto pode ser maior, mas
buf_size
pode ser coletado, resultando em erro ofuscado, como acima,Portanto, considerando todas essas preocupações, escrevi funções separadas:
Antes de tudo, vamos definir as próximas funções utilitárias:
ceil_division
para fazer divisão com teto (em contraste com a//
divisão padrão com piso, mais informações podem ser encontradas neste tópico )split
para dividir a string pelo separador fornecido da extremidade direita, com capacidade de mantê-la:read_batch_from_end
ler lote da extremidade direita do fluxo binárioDepois disso, podemos definir a função para ler o fluxo de bytes em ordem inversa, como
e, finalmente, uma função para reverter o arquivo de texto pode ser definida como:
Testes
Preparações
Eu criei 4 arquivos usando o
fsutil
comando :Também refatorei a solução @srohde para trabalhar com o objeto de arquivo em vez do caminho do arquivo.
Script de teste
Nota : Eu usei
collections.deque
classe para esgotar o gerador.Saídas
Para o PyPy 3.5 no Windows 10:
Para o CPython 3.5 no Windows 10:
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.0
versão dolz
pacote (requer Python 3.5 +) que possui muitos utilitários funcionais / de iteração bem testados.Pode ser usado como
Ele suporta todas as codificações padrão (talvez, exceto
utf-7
porque é difícil para mim definir uma estratégia para gerar cadeias codificáveis com ela).fonte
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.
fonte
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:
Gostaria de mudar para:
Aqui está uma resposta modificada que deseja manipular um arquivo e mantém novas linhas:
fonte
uma função simples para criar um segundo arquivo invertido (somente linux):
Como usar
fonte
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.Se você estiver preocupado com o tamanho do arquivo / uso da memória, mapear a memória do arquivo e digitalizar para trás em busca de novas linhas é uma solução:
Como procurar uma string em arquivos de texto?
fonte
com aberto ("nome do arquivo") como f:
fonte
list(reversed(f.read()))
.fonte
Sempre use
with
ao trabalhar com arquivos, pois lida com tudo para você:Ou no Python 3:
fonte
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
fonte
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.
fonte
Leia o arquivo linha por linha e adicione-o em uma lista na ordem inversa.
Aqui está um exemplo de código:
fonte
fonte
usar:
fonte
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
fonte