Question

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 énorme fichier texte (~15MB) avec des lignes de longueur inconnue, mais de longueur différente, et que je dois sauter à une ligne particulière, numéro que je connais 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 une.

Était-ce utile?

La solution

linecache :

  

Le module linecache permet d'en obtenir ligne depuis un fichier source Python, lors d’une tentative d’optimisation interne, à l’aide d’un cache, cas courant où de nombreuses lignes sont lues à partir d’un seul fichier. Ceci est utilisé par le module traceback pour récupérer les lignes source à inclure dans la trace formatée ...

Autres conseils

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 pouvez 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 ET réduire l'utilisation de la mémoire en modifiant le dernier paramètre en " open " à quelque chose de pas 0.

0 signifie que l'opération de lecture de fichier n'est pas mise en tampon, ce qui est très lent et utilise beaucoup de disque. 1 signifie que le fichier est mis en mémoire tampon, ce qui constituerait une amélioration. Tout ce qui est supérieur à 1 (disons 8k .. c'est-à-dire 8096 ou supérieur) lit des morceaux du fichier en mémoire. Vous y accédez toujours par for line in open(etc):, mais python ne va que petit à petit, en supprimant chaque bloc mis en mémoire tampon après son traitement.

Je suis probablement gâté par un bélier abondant, 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 pas d'autre choix que de parcourir toutes les lignes avant votre ligne de départ. Tout ce que vous pouvez faire est de le rendre beau. 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'index 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 toutes les autres lignes 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 atteindre cette position.

Modifier : vous pouvez utiliser le linecache. getline (nomfichier, lineno) , qui renvoie le contenu de la ligne lineno, mais seulement après avoir lu le fichier entier en mémoire. Bon si vous accédez de manière aléatoire aux lignes à partir du fichier (comme python lui-même pourrait vouloir faire pour imprimer une trace), mais pas pour un fichier de 15 Mo.

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 plusieurs fois dans le même fichier et que vous savez que le fichier ne change pas, vous pouvez le faire:
Tout d’abord, parcourez l’ensemble du fichier et enregistrez le & Quot-search-location & Quot; des numéros de lignes clés (tels que, par exemple, 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. et ainsi de suite

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 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.

  • Quelle ligne voulez-vous?
  • Calculez le décalage d'octet du numéro de ligne correspondant dans le fichier d'index (possible car la taille de ligne du fichier d'index est constante).
  • Utilisez search ou ce que vous voulez pour passer directement à la ligne du fichier d'index.
  • Analyse pour obtenir un décalage d'octet pour la ligne correspondante du fichier réel.

J'ai eu le même problème (besoin de récupérer une énorme ligne spécifique à un 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 un nombre pluriel de lignes spécifiques. Cela a résolu le problème principal: comment traiter directement à l’endroit nécessaire du fichier.

J'ai découvert la prochaine décision: Dans un premier temps, j'ai terminé le dictionnaire avec la position de départ de chaque ligne (la clé correspond au numéro de la ligne et la valeur & # 8211; la longueur cumulée des lignes précédentes).

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

finalement, 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) & # 8211; commande qui exécute l’élagage du fichier jusqu’à la création de la ligne. Donc, si vous commettez ensuite readline & # 8211; vous obtenez votre ligne cible.

En utilisant une telle approche, j’ai gagné un temps considérable.

Les lignes elles-mêmes contiennent-elles des informations d’index? Si le contenu de chaque ligne était quelque chose comme & Quot; <line index>:Data & Quot ;, alors l'approche seek() pourrait être utilisée pour effectuer une recherche binaire dans le fichier, même si la quantité 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 souhaité, etc.

Sinon, le mieux que vous puissiez faire est simplement readlines(). Si vous ne souhaitez 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 vers <=>.

Voici un exemple d'utilisation de 'readlines (sizehint)' pour lire un bloc de lignes à la fois. DNS a souligné cette solution. J'ai écrit cet exemple car 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)

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 vous déplacer sur la ligne dont vous avez besoin

Si vous traitez avec un fichier texte & amp; basé sur système linux , vous pouvez utiliser 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)

Peut utiliser cette fonction pour renvoyer la ligne n:

def skipton(infile, n):
    with open(infile,'r') as fi:
        for i in range(n-1):
            fi.next()
        return fi.next()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top