J'ai un fichier texte qui contient un horodatage sur chaque ligne. Mon but est de trouver la plage de temps. Toutes les heures sont en ordre, de sorte que la première ligne sera l'heure la plus ancienne et la dernière ligne, l'heure la plus tardive. Je n'ai besoin que de la toute première et dernière ligne. Quel serait le moyen le plus efficace d’obtenir ces lignes en python?
Remarque: Ces fichiers ont une longueur relativement importante, environ 1 à 2 millions de lignes et je dois le faire pour plusieurs centaines de fichiers.
with open(fname, 'rb') as fh:
first = next(fh).decode()
fh.seek(-1024, 2)
last = fh.readlines()[-1].decode()
La valeur de la variable ici est 1024: elle représente la longueur moyenne de la chaîne. Je choisis 1024 seulement par exemple. Si vous avez une estimation de la longueur de ligne moyenne, vous pouvez simplement utiliser cette valeur fois 2.
Puisque vous n'avez aucune idée de la limite supérieure possible pour la longueur de la ligne, la solution évidente serait de faire une boucle sur le fichier:
for line in fh:
pass
last = line
Vous n'avez pas besoin de vous soucier de l'indicateur binaire, vous pouvez simplement utiliser open(fname)
.
[~ # ~] eta [~ # ~] : Puisque vous avez beaucoup de fichiers sur lesquels travailler, vous pouvez créer un échantillon de plusieurs dizaines de fichiers. en utilisant random.sample
et exécutez ce code sur eux pour déterminer la longueur de la dernière ligne. Avec une valeur a priori importante du décalage de position (disons 1 Mo). Cela vous aidera à estimer la valeur pour le cycle complet.
Vous pouvez ouvrir le fichier en lecture et lire la première ligne à l'aide de la commande readline()
), puis rechercher la fin du fichier et revenir en arrière jusqu'à ce que vous trouviez la ligne précédente EOL et lisez la dernière ligne à partir de là.
with open(file, "rb") as f:
first = f.readline() # Read the first line.
f.seek(-2, os.SEEK_END) # Jump to the second last byte.
while f.read(1) != b"\n": # Until EOL is found...
f.seek(-2, os.SEEK_CUR) # ...jump back the read byte plus one more.
last = f.readline() # Read last line.
Passer à l'avant-dernier octet au lieu du dernier empêche que vous reveniez directement à cause d'un EOL final. Lorsque vous revenez en arrière, vous voudrez également passer à deux octets, car la lecture et la vérification de la fin de vie avancent d’un cran.
Lors de l'utilisation de seek
le format est fseek(offset, whence=0)
où whence
signifie à quoi le le décalage est relatif à. Citation de docs.python.org :
SEEK_SET
ou0
= chercher depuis le début du flux (valeur par défaut); offset doit être soit un nombre renvoyé par TextIOBase.tell () , soit zéro. Toute autre valeur de décalage produit un comportement indéfini.SEEK_CUR
ou1
= “chercher” à la position actuelle; offset doit être égal à zéro, ce qui est une opération sans opération (toutes les autres valeurs ne sont pas prises en charge).SEEK_END
ou2
= chercher à la fin du flux; offset doit être égal à zéro (toutes les autres valeurs ne sont pas prises en charge).
Le parcourir 10 fois par timeit sur un fichier de 6 000 lignes totalisant 200 Ko m'a donné 1,62 s contre 6,92 secondes par rapport à la boucle for inférieure suggérée plus tôt. En utilisant un fichier de 1,3 Go, contenant toujours 6 000 lignes, cent fois, on obtient 8,93 vs 86,95.
with open(file, "rb") as f:
first = f.readline() # Read the first line.
for last in f: pass # Loop through the whole file reading it all.
Voici une version modifiée de la réponse de SilentGhost qui fera ce que vous voulez.
with open(fname, 'rb') as fh:
first = next(fh)
offs = -100
while True:
fh.seek(offs, 2)
lines = fh.readlines()
if len(lines)>1:
last = lines[-1]
break
offs *= 2
print first
print last
Pas besoin de limite supérieure pour la longueur de ligne ici.
Pouvez-vous utiliser des commandes unix? Je pense que l'utilisation de head -1
Et de tail -n 1
Sont probablement les méthodes les plus efficaces. Alternativement, vous pouvez utiliser une simple fid.readline()
pour obtenir la première ligne et fid.readlines()[-1]
, mais cela risque de prendre trop de mémoire.
C'est ma solution, compatible aussi avec Python3. Il gère également les cas à la frontière, mais il manque le support utf-16:
def tail(filepath):
"""
@author Marco Sulla ([email protected])
@date May 31, 2016
"""
try:
filepath.is_file
fp = str(filepath)
except AttributeError:
fp = filepath
with open(fp, "rb") as f:
size = os.stat(fp).st_size
start_pos = 0 if size - 1 < 0 else size - 1
if start_pos != 0:
f.seek(start_pos)
char = f.read(1)
if char == b"\n":
start_pos -= 1
f.seek(start_pos)
if start_pos == 0:
f.seek(start_pos)
else:
char = ""
for pos in range(start_pos, -1, -1):
f.seek(pos)
char = f.read(1)
if char == b"\n":
break
return f.readline()
Il est inspiré par réponse de Trasp et commentaire de AnotherParker .
Commencez par ouvrir le fichier en mode lecture. Utilisez ensuite la méthode readlines () pour lire ligne par ligne.Toutes les lignes stockées dans une liste.Maintenant, vous pouvez utiliser des tranches de liste pour obtenir la première et la dernière ligne du fichier.
a=open('file.txt','rb')
lines = a.readlines()
if lines:
first_line = lines[:1]
last_line = lines[-1]
w=open(file.txt, 'r')
print ('first line is : ',w.readline())
for line in w:
x= line
print ('last line is : ',x)
w.close()
La boucle for
parcourt les lignes et x
obtient la dernière ligne de la dernière itération.
Personne n'a mentionné utiliser l'inverse:
f=open(file,"r")
r=reversed(f.readlines())
last_line_of_file = r.next()
Voici une extension de la réponse de @ Trasp qui dispose d'une logique supplémentaire pour gérer la casse de coin d'un fichier ne comportant qu'une seule ligne. Il peut être utile de gérer ce cas si vous souhaitez lire de manière répétée la dernière ligne d'un fichier continuellement mis à jour. Sans cela, si vous essayez de saisir la dernière ligne d'un fichier qui vient d'être créé et ne contient qu'une seule ligne, IOError: [Errno 22] Invalid argument
sera soulevé.
def tail(filepath):
with open(filepath, "rb") as f:
first = f.readline() # Read the first line.
f.seek(-2, 2) # Jump to the second last byte.
while f.read(1) != b"\n": # Until EOL is found...
try:
f.seek(-2, 1) # ...jump back the read byte plus one more.
except IOError:
f.seek(-1, 1)
if f.tell() == 0:
break
last = f.readline() # Read last line.
return last
with open("myfile.txt") as f:
lines = f.readlines()
first_row = lines[0]
print first_row
last_row = lines[-1]
print last_row
Obtenir la première ligne est trivialement facile. Pour la dernière ligne, supposons que vous connaissiez une limite supérieure approximative sur la longueur de la ligne, os.lseek une certaine quantité de SEEK_END
trouve l’avant-dernière ligne qui se termine puis readline () la dernière ligne.
with open(filename, "r") as f:
first = f.readline()
if f.read(1) == '':
return first
f.seek(-2, 2) # Jump to the second last byte.
while f.read(1) != b"\n": # Until EOL is found...
f.seek(-2, 1) # ...jump back the read byte plus one more.
last = f.readline() # Read last line.
return last
La réponse ci-dessus est une version modifiée des réponses ci-dessus qui gère le cas où il n'y a qu'une seule ligne dans le fichier.