Lire un fichier dans l'ordre inverse en utilisant python
Comment lire un fichier dans l'ordre inverse en utilisant python? Je veux lire un fichier de la dernière ligne à la première.
15 réponses
for line in reversed(open("filename").readlines()):
print line.rstrip()
et en Python 3:
for line in reversed(list(open("filename"))):
print(line.rstrip())
Une bonne, réponse efficace à l'écrit comme un générateur.
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 concact the segment to the last line of new chunk
# instead, yield the segment first
if buffer[-1] is not '\n':
lines[-1] += segment
else:
yield segment
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
Que Diriez-vous de quelque chose comme ceci:
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
Depuis le fichier est en lecture caractère par caractère dans l'ordre inverse, il fonctionnera même sur de très gros fichiers, tant que les lignes individuelles tenir en mémoire.
vous pouvez également utiliser le module python file_read_backwards
.
après l'installation, via pip install file_read_backwards
(v1.2.1), vous pouvez lire le dossier entier à l'envers (ligne-wise) d'une manière efficace de la mémoire via:
#!/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
il supporte les encodages"utf-8"," latin-1", et" ascii".
est également disponible pour python3. D'autres documents peuvent être trouvés à http://file-read-backwards.readthedocs.io/en/latest/readme.html
for line in reversed(open("file").readlines()):
print line.rstrip()
si vous êtes sous linux, vous pouvez utiliser la commande tac
.
$ tac file
2 recettes que vous pouvez trouver dans ActiveState ici et ici
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)
ici vous pouvez trouver mon implémentation my, vous pouvez limiter l'utilisation de la ram en changeant la variable" buffer", il y a un bug que le programme affiche une ligne vide au début.
et aussi l'utilisation de la ram peut être augmentée s'il n'y a pas de nouvelles lignes pour plus que des octets tampons, la variable" leak "va augmenter jusqu'à voir une nouvelle ligne ("\n").
cela fonctionne aussi pour les fichiers de 16 Go qui sont plus gros que ma mémoire totale.
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
Merci pour la réponse @srohde. Il a une petite vérification de bogue pour le caractère newline Avec " is " opérateur, et je ne pouvais pas commenter la réponse avec 1 réputation. Aussi, je voudrais gérer le fichier ouvert à l'extérieur parce que cela me permet d'intégrer mes divagations pour les tâches luigi.
ce que j'avais besoin de changer a la forme:
with open(filename) as fp:
for line in fp:
#print line, # contains new line
print '>{}<'.format(line)
j'aimerais changer pour:
with open(filename) as fp:
for line in reversed_fp_iter(fp, 4):
#print line, # contains new line
print '>{}<'.format(line)
Voici une réponse modifiée qui veut un fichier poignée et garde des retours à la ligne:
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
une fonction simple pour créer un second fichier inversé (linux seulement):
import os
def tac(file1, file2):
print(os.system('tac %s > %s' % (file1,file2)))
comment utiliser
tac('ordered.csv', 'reversed.csv')
f = open('reversed.csv')
si vous êtes préoccupé par la taille du fichier / l'utilisation de la mémoire, la cartographie de la mémoire du fichier et la recherche de nouvelles lignes à l'envers est une solution:
Comment rechercher une chaîne de caractères dans des fichiers texte?
def reverse_lines(filename):
y=open(filename).readlines()
return y[::-1]
toujours utiliser with
quand vous travaillez avec des fichiers car il gère tout pour vous:
with open('filename', 'r') as f:
for line in reversed(f.readlines()):
print line
ou en Python 3:
with open('filename', 'r') as f:
for line in reversed(list(f.readlines())):
print(line)
vous devez d'abord ouvrir votre fichier en format read, le sauvegarder sur une variable, puis ouvrir le second fichier en format write où vous écririez ou ajouteriez la variable en utilisant un [: -1] slice, inversant complètement le fichier. Vous pouvez également utiliser readlines() pour faire une liste de lignes, vous pouvez manipuler
def copy_and_reverse(filename, newfile):
with open(filename) as file:
text = file.read()
with open(newfile, "w") as file2:
file2.write(text[::-1])
la plupart des réponses doivent lire le dossier complet avant de faire quoi que ce soit. Cet échantillon contient des échantillons de plus en plus grands à partir de la fin .
J'ai seulement vu la réponse de Murat Yükselen en écrivant cette réponse. C'est presque le même, qui, je suppose, est une bonne chose. L'exemple ci-dessous traite aussi avec \r et augmente sa taille de tampon à chaque étape. J'ai aussi quelques tests unitaires pour sauvegarder ce code.
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
j'ai dû le faire il y a quelques temps et utilisé le code ci-dessous. Il des tuyaux pour le shell. Je crains de ne plus avoir le script complet. Si vous êtes sur un système d'exploitation unixish, vous pouvez utiliser "tac", cependant sur par exemple Mac OSX tac commande ne fonctionne pas, utilisez tail-R. L'extrait de code ci-dessous teste sur quelle plate-forme vous êtes, et ajuste la commande en conséquence
# 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)