Existe-t-il des alternatives au code ci-dessous:
startFromLine = 141978 # or whatever line I need to jump to
urlsfile = open(filename, "rb", 0)
linesCounter = 1
for line in urlsfile:
if linesCounter > startFromLine:
DoSomethingWithThisLine(line)
linesCounter += 1
Si je traite un fichier texte volumineux (~15MB)
avec des lignes de longueur inconnue, mais différente, et que je dois passer à une ligne particulière dont je connais le numéro par avance Je me sens mal en les traitant un par un quand je sais que je pourrais ignorer au moins la première moitié du fichier. Vous recherchez une solution plus élégante s'il y en a.
Le module
linecache
permet d’obtenir n'importe quelle ligne d’un fichier source Python, tout en essayant d’optimiser en interne, à l’aide d’un cache, cas typique où plusieurs lignes sont lues à partir d’un seul fichier. Ceci est utilisé par le moduletraceback
pour récupérer les lignes source à inclure dans la trace formatée ...
Vous ne pouvez pas avancer sans lire le fichier au moins une fois, car vous ne savez pas où se trouvent les sauts de ligne. Vous pourriez faire quelque chose comme:
# Read in the file once and build a list of line offsets
line_offset = []
offset = 0
for line in file:
line_offset.append(offset)
offset += len(line)
file.seek(0)
# Now, to skip to line n (with the first line being line 0), just do
file.seek(line_offset[n])
Vous n'avez pas vraiment beaucoup d'options si les lignes sont de longueurs différentes ... vous devez malheureusement traiter les caractères de fin de ligne pour savoir quand vous avez progressé vers la ligne suivante.
Vous pouvez toutefois accélérer considérablement cette opération ET réduire l'utilisation de la mémoire en modifiant le dernier paramètre "ouvert" sur un paramètre différent de 0.
0 signifie que la lecture du fichier n'a pas de mémoire tampon, ce qui est très lent et nécessite beaucoup de disque. 1 signifie que le fichier est mis en mémoire tampon, ce qui constituerait une amélioration. Tout ce qui dépasse 1 (disons 8k .. c'est-à-dire 8096 ou plus) lit des morceaux du fichier en mémoire. Vous y accédez toujours via for line in open(etc):
, mais python ne va que petit à petit, en supprimant chaque morceau mis en mémoire tampon après son traitement.
Je suis probablement gâté par l'abondance de bélier, mais 15 M n'est pas énorme. Lire dans la mémoire avec readlines()
est ce que je fais habituellement avec des fichiers de cette taille. Accéder à une ligne après cela est trivial.
Puisqu'il n'y a aucun moyen de déterminer la longueur de toutes les lignes sans les lire, vous n'avez d'autre choix que de parcourir toutes les lignes avant votre ligne de départ. Tout ce que vous pouvez faire est de lui donner une apparence agréable. Si le fichier est vraiment énorme, vous pouvez utiliser une approche basée sur un générateur:
from itertools import dropwhile
def iterate_from_line(f, start_from_line):
return (l for i, l in dropwhile(lambda x: x[0] < start_from_line, enumerate(f)))
for line in iterate_from_line(open(filename, "r", 0), 141978):
DoSomethingWithThisLine(line)
Remarque: l'indice est basé sur zéro dans cette approche.
Je suis surpris que personne ne mentionne Islice
line = next(itertools.islice(Fhandle,index_of_interest,index_of_interest+1),None) # just the one line
ou si vous voulez tout le reste du fichier
rest_of_file = itertools.islice(Fhandle,index_of_interest)
for line in rest_of_file:
print line
ou si vous voulez chaque ligne du fichier
rest_of_file = itertools.islice(Fhandle,index_of_interest,None,2)
for odd_line in rest_of_file:
print odd_line
Si vous connaissez à l'avance la position dans le fichier (plutôt le numéro de ligne), vous pouvez utiliser file.seek () pour aller à cette position.
Edit: vous pouvez utiliser la fonction linecache.getline (nomfichier, lineno) , qui renverra le contenu de la ligne lineno, mais uniquement après avoir lu le fichier entier en mémoire. Bon si vous accédez de façon aléatoire aux lignes à partir du fichier (comme python lui-même pourrait vouloir faire pour imprimer une trace), mais pas bon pour un fichier de 15 Mo.
Qu'est-ce qui génère le fichier que vous voulez traiter? Si c'est quelque chose sous votre contrôle, vous pouvez générer un index (quelle ligne correspond à quelle position.) Au moment de l'ajout du fichier. Le fichier d'index peut avoir une taille de ligne fixe (espace ou 0 chiffre) et sera certainement plus petit. Et peut donc être lu et traité rapidement.
Si vous ne souhaitez pas lire l'intégralité du fichier en mémoire, vous devrez peut-être utiliser un format autre que le texte brut.
bien sûr, tout dépend de ce que vous essayez de faire et de la fréquence à laquelle vous allez parcourir le fichier.
Par exemple, si vous allez sauter aux lignes plusieurs fois dans le même fichier et que vous savez que le fichier ne change pas lorsqu'il est utilisé, procédez comme suit:
Tout d’abord, parcourez l’ensemble du fichier et enregistrez "l’emplacement de recherche" de certains numéros de lignes de clés (par exemple, jamais moins de 1000 lignes),
Ensuite, si vous voulez la ligne 12005, passez à la position 12000 (que vous avez enregistrée), puis lisez 5 lignes et vous saurez que vous êtes sur la ligne 12005 , Etc., etc.
J'ai eu le même problème (besoin de récupérer d'énormes lignes spécifiques au fichier).
Bien sûr, je peux à chaque fois parcourir tous les enregistrements du fichier et l'arrêter lorsque le compteur sera égal à la ligne cible, mais cela ne fonctionne pas efficacement dans le cas où vous souhaitez obtenir plusieurs lignes spécifiques. Cela a résolu le problème principal - comment gérer directement à la place nécessaire du fichier.
J'ai découvert la décision suivante:.
t = open(file,’r’)
dict_pos = {}
kolvo = 0
length = 0
for each in t:
dict_pos[kolvo] = length
length = length+len(each)
kolvo = kolvo+1
en fin de compte, la fonction de visée:
def give_line(line_number):
t.seek(dict_pos.get(line_number))
line = t.readline()
return line
t.seek (line_number) - commande qui exécute l’élagage du fichier jusqu’à la création de la ligne. .__ Donc, si vous validez ensuite readline - vous obtenez votre ligne cible.
En utilisant une telle approche, j'ai économisé une partie importante du temps.
Les lignes elles-mêmes contiennent-elles des informations d'index? Si le contenu de chaque ligne était quelque chose comme "<line index>:Data
", l'approche seek()
pourrait alors être utilisée pour effectuer une recherche binaire dans le fichier, même si le montant de Data
est variable. Vous voudriez chercher jusqu'au milieu du fichier, lire une ligne, vérifier si son index est supérieur ou inférieur à celui que vous voulez, etc.
Autrement, le mieux que vous puissiez faire est simplement readlines()
. Si vous ne voulez pas lire tous les 15 Mo, vous pouvez utiliser l'argument sizehint
pour remplacer au moins un grand nombre de readline()
s par un nombre plus petit d'appels à readlines()
.
Si vous utilisez un fichier text & basé sur linux system , utilisez les commandes linux.
Pour moi, cela a bien fonctionné!
import commands
def read_line(path, line=1):
return commands.getoutput('head -%s %s | tail -1' % (line, path))
line_to_jump = 141978
read_line("path_to_large_text_file", line_to_jump)
Vous pouvez utiliser mmap pour trouver le décalage des lignes. MMap semble être le moyen le plus rapide de traiter un fichier
exemple:
with open('input_file', "r+b") as f:
mapped = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
i = 1
for line in iter(mapped.readline, ""):
if i == Line_I_want_to_jump:
offsets = mapped.tell()
i+=1
puis utilisez f.seek (décalages) pour passer à la ligne dont vous avez besoin
Voici un exemple utilisant 'readlines (sizehint)' pour lire un bloc de lignes à la fois. DNS a souligné cette solution. J'ai écrit cet exemple parce que les autres exemples ici sont orientés sur une seule ligne.
def getlineno(filename, lineno):
if lineno < 1:
raise TypeError("First line is line 1")
f = open(filename)
lines_read = 0
while 1:
lines = f.readlines(100000)
if not lines:
return None
if lines_read + len(lines) >= lineno:
return lines[lineno-lines_read-1]
lines_read += len(lines)
print getlineno("nci_09425001_09450000.smi", 12000)